Compare commits

...

580 Commits

Author SHA1 Message Date
Frappe PR Bot
b7d8bfc58c chore(release): Bumped to Version 14.61.3
## [14.61.3](https://github.com/frappe/erpnext/compare/v14.61.2...v14.61.3) (2024-01-30)

### Bug Fixes

* Asset Depreciation WDV as per Income Tax Act ([b840eb9](b840eb90eb))
* asset module test cases ([f604798](f604798a45))
* **Batch:** reload doc after splitting ([c759406](c759406ebb))
* default enable closing stock balance (backport [#39551](https://github.com/frappe/erpnext/issues/39551)) ([#39553](https://github.com/frappe/erpnext/issues/39553)) ([1e32c62](1e32c6207e))
* do not auto-populate item delivery date ([1e341f0](1e341f0ff6))
* do not consider rejected warehouses in pick list ([#39539](https://github.com/frappe/erpnext/issues/39539)) ([f6725e4](f6725e4342))
* do not delete batches implicitly ([9a5995a](9a5995a3e5))
* **ecom:** do not create a new contact if a contact already exists ([#39290](https://github.com/frappe/erpnext/issues/39290)) ([47c591c](47c591ccf1))
* email list for auto reorder material request ([780c069](780c069268))
* enqueue JV submission when more than 100 accounts ([66be3c5](66be3c551f))
* fetch correct quantity and amount for grouped asset ([1dacb79](1dacb79441))
* incorrect amount in the material request item (backport [#39567](https://github.com/frappe/erpnext/issues/39567)) ([#39568](https://github.com/frappe/erpnext/issues/39568)) ([c26f7bb](c26f7bbed0))
* linter issue ([941f882](941f8824e5))
* not able to edit / change address from portal ([e3fdb6f](e3fdb6f55c))
* not able to edit address through portal ([b310a55](b310a55727))
* Payment Terms Status for Sales Order report should show all payment terms from order not only this comming from template ([2953959](295395918c))
* perf issue while submitting stock entry (backport [#39634](https://github.com/frappe/erpnext/issues/39634)) ([#39641](https://github.com/frappe/erpnext/issues/39641)) ([3ee0555](3ee0555115))
* prevent extra transfer against inter transfer transaction (backport [#39213](https://github.com/frappe/erpnext/issues/39213)) ([#39595](https://github.com/frappe/erpnext/issues/39595)) ([bf61030](bf61030dab))
* qtn tests using delivery date ([c50988b](c50988b1bc))
* return doc obj after submit ([0472879](04728792f5))
* RM valuation rate in SCR ([#39541](https://github.com/frappe/erpnext/issues/39541)) ([9fd1692](9fd1692db2))
* typo's and parameter changes ([41c074d](41c074d0bb))
2024-01-30 14:14:00 +00:00
rohitwaghchaure
378866f429 Merge pull request #39640 from frappe/version-14-hotfix
chore: release v14
2024-01-30 19:42:12 +05:30
mergify[bot]
3ee0555115 fix: perf issue while submitting stock entry (backport #39634) (#39641)
fix: perf issue while submitting stock entry (#39634)

(cherry picked from commit b14886b227)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-30 15:44:03 +05:30
rohitwaghchaure
f6725e4342 fix: do not consider rejected warehouses in pick list (#39539)
* fix: do not picked rejected materials

* test: test case for pick list without rejected materials
2024-01-30 11:25:20 +05:30
ruthra kumar
d4bd508b62 Merge pull request #39632 from frappe/mergify/bp/version-14-hotfix/pr-39559
fix: prevent Return Invoices(Credit/Debit Note) from using a different account  (backport #39559)
2024-01-30 11:23:39 +05:30
ruthra kumar
0884c5ed83 chore: resolve conflicts 2024-01-30 11:05:24 +05:30
ruthra kumar
9212a74913 test: debit note account mismatch
(cherry picked from commit bdca718103)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
2024-01-30 05:31:44 +00:00
ruthra kumar
faeca79c68 test: account mismatch validation
(cherry picked from commit 8bdc760733)
2024-01-30 05:31:43 +00:00
ruthra kumar
c5ce4db315 refactor: prevent '{debit/credit}_to' account mismatch
(cherry picked from commit 6f2fae1b61)

# Conflicts:
#	erpnext/controllers/accounts_controller.py
2024-01-30 05:31:43 +00:00
ruthra kumar
844db3b8d6 Merge pull request #39509 from frappe/mergify/bp/version-14-hotfix/pr-39054
refactor: provision to filter on dimensions in reconciliation tool (backport #39054)
2024-01-30 08:20:51 +05:30
ruthra kumar
d0c810accd refactor(test): disable dimensions post test 2024-01-30 06:29:59 +05:30
ruthra kumar
1966ea15ba refactor: pass orders name in a separate criterion 2024-01-30 05:58:26 +05:30
mergify[bot]
d794502681 fix amount not updated when change rate in material request (backport #39606) (backport #39614) (#39621)
fix amount not updated when change rate in material request (backport #39606) (#39614)

fix amount not updated when change rate in material request (#39606)

* fix amount not updated when change rate in material request

* make code consistent

(cherry picked from commit efade9b9ae)

Co-authored-by: Jeffry Suryadharma <41689493+jeffrysurya@users.noreply.github.com>
(cherry picked from commit 2389b41f51)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-29 18:57:40 +05:30
ruthra kumar
c054316127 chore: fix typo and initialize a list 2024-01-29 17:41:56 +05:30
ruthra kumar
ff0daedd52 refactor: convert sql to query builder on Payments query 2024-01-29 17:09:59 +05:30
Gursheen Kaur Anand
50d2381781 Merge pull request #39617 from frappe/mergify/bp/version-14-hotfix/pr-39616
fix(minor): do not auto-populate item delivery date from qtn (backport #39616)
2024-01-29 16:56:10 +05:30
Gursheen Anand
c50988b1bc fix: qtn tests using delivery date
(cherry picked from commit 079cd30b9c)
2024-01-29 10:22:04 +00:00
Gursheen Anand
1e341f0ff6 fix: do not auto-populate item delivery date
(cherry picked from commit 49cb11c1f3)
2024-01-29 10:22:02 +00:00
ruthra kumar
ea779fcad9 refactor: build payment entry query separately 2024-01-29 14:44:16 +05:30
mergify[bot]
2389b41f51 fix amount not updated when change rate in material request (backport #39606) (#39614)
fix amount not updated when change rate in material request (#39606)

* fix amount not updated when change rate in material request

* make code consistent

(cherry picked from commit efade9b9ae)

Co-authored-by: Jeffry Suryadharma <41689493+jeffrysurya@users.noreply.github.com>
2024-01-29 13:36:10 +05:30
Gursheen Kaur Anand
896bba4e4d Merge pull request #39611 from frappe/mergify/bp/version-14-hotfix/pr-39562
fix: enqueue JV submission when > 100 accounts (backport #39562)
2024-01-29 12:38:37 +05:30
Gursheen Anand
04728792f5 fix: return doc obj after submit
(cherry picked from commit fc677811b7)
2024-01-29 06:10:04 +00:00
Gursheen Anand
66be3c551f fix: enqueue JV submission when more than 100 accounts
(cherry picked from commit 53b44ccf29)
2024-01-29 06:10:04 +00:00
Nabin Hait
fbf34439c1 Merge pull request #39565 from frappe/mergify/bp/version-14-hotfix/pr-39489
fix: fetch correct quantity and amount for grouped asset (backport #39489)
2024-01-29 11:38:27 +05:30
mergify[bot]
bf61030dab fix: prevent extra transfer against inter transfer transaction (backport #39213) (#39595)
* fix: prevent extra transfer against inter transfer transaction (#39213)

* fix: prevent extra transfer against inter transfer transaction

* fix: internal transfer dashboard

(cherry picked from commit 8fdc244e16)

# Conflicts:
#	erpnext/controllers/stock_controller.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-27 23:25:04 +05:30
ruthra kumar
bdf5c02d33 Merge pull request #39593 from frappe/mergify/bp/version-14-hotfix/pr-39591
refactor: Do proper currency conversion on Future Payments column in AR/AP report (backport #39591)
2024-01-27 13:19:37 +05:30
ruthra kumar
12ac371b22 test: future payment with foreign currency
(cherry picked from commit 7b37389115)
2024-01-27 07:23:08 +00:00
ruthra kumar
4626ea79ef refactor: do currency conversion on future amount columns
(cherry picked from commit 0de4197c88)
2024-01-27 07:23:08 +00:00
Frappe PR Bot
33b21a54f7 chore(release): Bumped to Version 14.61.2
## [14.61.2](https://github.com/frappe/erpnext/compare/v14.61.1...v14.61.2) (2024-01-27)

### Bug Fixes

* incorrect amount in the material request item (backport [#39567](https://github.com/frappe/erpnext/issues/39567)) (backport [#39568](https://github.com/frappe/erpnext/issues/39568)) ([#39586](https://github.com/frappe/erpnext/issues/39586)) ([e729972](e729972987))
2024-01-27 04:43:52 +00:00
mergify[bot]
e729972987 fix: incorrect amount in the material request item (backport #39567) (backport #39568) (#39586)
fix: incorrect amount in the material request item (backport #39567) (#39568)

fix: incorrect amount in the material request item (#39567)

fix: incoorect amount in the material request
(cherry picked from commit 2bdfdeeb9a)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit c26f7bbed0)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-27 10:12:37 +05:30
Saqib Ansari
47c591ccf1 fix(ecom): do not create a new contact if a contact already exists (#39290) 2024-01-26 14:25:05 +05:30
Deepesh Garg
169c7f3e05 Merge pull request #39576 from frappe/mergify/bp/version-14-hotfix/pr-39511
refactor(Sales Invoice): set account and sum for payments (#39511)
2024-01-26 10:04:37 +05:30
barredterra
a072cfbf3f refactor(Sales Invoice): set account for mode of payment
(cherry picked from commit 3815f07c33)
2024-01-26 04:15:10 +00:00
s-aga-r
9fd1692db2 fix: RM valuation rate in SCR (#39541) 2024-01-25 19:08:39 +05:30
mergify[bot]
c26f7bbed0 fix: incorrect amount in the material request item (backport #39567) (#39568)
fix: incorrect amount in the material request item (#39567)

fix: incoorect amount in the material request
(cherry picked from commit 2bdfdeeb9a)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-25 17:22:15 +05:30
Nabin Hait
1dacb79441 fix: fetch correct quantity and amount for grouped asset
(cherry picked from commit 06f48c678b)
2024-01-25 10:51:48 +00:00
Nabin Hait
0d41631ae4 Merge pull request #39453 from nabinhait/asset-depr-income-tax-act
fix: Asset Depreciation WDV as per Income Tax Act
2024-01-25 16:06:22 +05:30
ruthra kumar
560af95931 Merge pull request #39497 from frappe/mergify/bp/version-14-hotfix/pr-35330
refactor: cr/dr note should be standalone even when created from another invoice (backport #35330)
2024-01-25 13:25:35 +05:30
mergify[bot]
1e32c6207e fix: default enable closing stock balance (backport #39551) (#39553)
fix: default enable closing stock balance (#39551)

(cherry picked from commit d1fb90edff)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-25 12:51:48 +05:30
rohitwaghchaure
822a5a842b Merge pull request #39548 from rohitwaghchaure/fix-do-not-delete-batch-implicitly
fix: do not delete batches implicitly
2024-01-25 12:33:28 +05:30
Rohit Waghchaure
9a5995a3e5 fix: do not delete batches implicitly 2024-01-25 11:58:07 +05:30
Nabin Hait
941f8824e5 fix: linter issue 2024-01-25 11:40:44 +05:30
ruthra kumar
22c13b1b83 Merge pull request #39537 from frappe/mergify/bp/version-14-hotfix/pr-39331
fix: Payment Terms Status for Sales Order report show all payment terms from orders (not only when there is a payment terms template) (backport #39331)
2024-01-24 16:33:50 +05:30
Florian HENRY
295395918c fix: Payment Terms Status for Sales Order report should show all payment terms from order not only this comming from template
(cherry picked from commit 6c8f52b26f)
2024-01-24 10:42:57 +00:00
ruthra kumar
2c431f394e chore: resolve conflicts 2024-01-24 16:11:14 +05:30
rohitwaghchaure
924fc8f0a8 Merge pull request #39530 from frappe/mergify/bp/version-14-hotfix/pr-39525
fix: email list for auto reorder material request (backport #39525)
2024-01-24 14:49:18 +05:30
Rohit Waghchaure
780c069268 fix: email list for auto reorder material request
(cherry picked from commit 764f3422a0)
2024-01-24 07:07:05 +00:00
rohitwaghchaure
f4123e1863 Merge pull request #39528 from rohitwaghchaure/fixed-not-able-to-edit-change-address
fix: not able to edit / change address from portal
2024-01-24 12:19:45 +05:30
rohitwaghchaure
a1559ed0c2 Merge pull request #39526 from frappe/mergify/bp/version-14-hotfix/pr-39521
fix: not able to edit address through portal (backport #39521)
2024-01-24 12:16:57 +05:30
Rohit Waghchaure
e3fdb6f55c fix: not able to edit / change address from portal 2024-01-24 12:15:55 +05:30
Rohit Waghchaure
b310a55727 fix: not able to edit address through portal
(cherry picked from commit b046d980ad)
2024-01-24 06:20:29 +00:00
Raffael Meyer
16dc8232f2 Merge pull request #39514 from barredterra/refactor-batch-v14 2024-01-23 19:01:53 +01:00
barredterra
dd3b77ae28 refactor(Batch): use const instead of var 2024-01-23 15:08:49 +01:00
barredterra
c759406ebb fix(Batch): reload doc after splitting
to show updated qty
2024-01-23 15:08:33 +01:00
ruthra kumar
e70f0f6d8d refactor: handle dynamic dimension in order query
(cherry picked from commit 7c2cb70387)
2024-01-23 11:59:31 +00:00
ruthra kumar
9a3bde9350 refactor: update dimensions, only if provided
(cherry picked from commit ec0f17ca8b)
2024-01-23 11:59:31 +00:00
ruthra kumar
51bc225fe5 refactor: dynamic dimension filters in pop up
(cherry picked from commit f8bbb0619c)
2024-01-23 11:59:31 +00:00
ruthra kumar
15db7b8ae4 test: dimension inheritance on adv allocation
(cherry picked from commit fcf4687c52)
2024-01-23 11:59:30 +00:00
ruthra kumar
d4828f3cf5 refactor: pass dimensions on advance allocation
(cherry picked from commit cbd443a78a)
2024-01-23 11:59:30 +00:00
ruthra kumar
ec58c309d2 test: dimension inheritance in PE reconciliation
(cherry picked from commit 6148fb024b)
2024-01-23 11:59:30 +00:00
ruthra kumar
a919702319 refactor: pass dimension values to Gain/Loss journal
(cherry picked from commit c44eb432a5)

# Conflicts:
#	erpnext/accounts/utils.py
2024-01-23 11:59:29 +00:00
ruthra kumar
de948f23c1 test: dimension inheritance for cr note reconciliation
(cherry picked from commit ba5a7c8cd8)
2024-01-23 11:59:29 +00:00
ruthra kumar
c1591ec8e1 chore: test dimension filter output
(cherry picked from commit e3c44231ab)
2024-01-23 11:59:29 +00:00
ruthra kumar
c3ffb7a4c4 refactor: apply dimension filters on cr/dr notes
(cherry picked from commit 188ff8cde7)
2024-01-23 11:59:28 +00:00
ruthra kumar
937262b572 refactor: Credit Note and its Exc gain/loss JE inherits dimensions
(cherry picked from commit ab939cc6e8)
2024-01-23 11:59:28 +00:00
ruthra kumar
41c074d0bb fix: typo's and parameter changes
(cherry picked from commit 0ec17590ae)
2024-01-23 11:59:28 +00:00
ruthra kumar
ecd36501af refactor: partial change on outstanding invoice popup
(cherry picked from commit 2154502955)
2024-01-23 11:59:28 +00:00
ruthra kumar
8f87c588ec refactor: replace sql with query builder for Jourals query
(cherry picked from commit 9c5a79209e)
2024-01-23 11:59:28 +00:00
ruthra kumar
dac422a0e1 refactor: pass dimension details to query
(cherry picked from commit 5dc22e1811)

# Conflicts:
#	erpnext/accounts/utils.py
2024-01-23 11:59:27 +00:00
ruthra kumar
b2db6d0546 refactor: set query filters for dimensions
(cherry picked from commit ad8475cb8b)
2024-01-23 11:59:27 +00:00
ruthra kumar
3d62bce885 refactor: pass dimension filters to query
(cherry picked from commit ff60ec85b8)

# Conflicts:
#	erpnext/controllers/accounts_controller.py
2024-01-23 11:59:27 +00:00
ruthra kumar
66cadb8b9f refactor: handle dimension filters
(cherry picked from commit c1fe4bcc64)
2024-01-23 11:59:26 +00:00
ruthra kumar
81b87ef2e2 refactor: column break in dimension section
(cherry picked from commit 20576e0f47)
2024-01-23 11:59:26 +00:00
ruthra kumar
4114c0e854 refactor: dimensions filter section in payment reconciliation
(cherry picked from commit 20e0acc20a)
2024-01-23 11:59:26 +00:00
ruthra kumar
b25e8ae14c refactor: update dimension doctypes in hooks
(cherry picked from commit cfb3d87267)
2024-01-23 11:59:25 +00:00
ruthra kumar
5c9ad21a3f refactor: dimensions section in allocation table in reconciliation
(cherry picked from commit 1cde804c77)
2024-01-23 11:59:25 +00:00
Frappe PR Bot
26ca27a431 chore(release): Bumped to Version 14.61.1
## [14.61.1](https://github.com/frappe/erpnext/compare/v14.61.0...v14.61.1) (2024-01-23)

### Bug Fixes

* linting issue ([6895b74](6895b74ecc))
* party field in pdf html ([a19b41d](a19b41d8c8))
* set unallocated amount after base tax ([f7ba736](f7ba7361ca))
* UOM needs to be whole number not being checked in quotations ([55c9cc3](55c9cc3f26))
* use most reliable section reference per report line ([3bdff18](3bdff18467))
2024-01-23 11:54:25 +00:00
ruthra kumar
f8f3c917d2 Merge pull request #39506 from frappe/version-14-hotfix
chore: release v14
2024-01-23 17:23:12 +05:30
ruthra kumar
956f05238a refactor: Payment btn criteria for Cr/Dr notes
(cherry picked from commit 60eee564bf)
2024-01-23 06:36:09 +00:00
ruthra kumar
1aeeac4d06 refactor: criteria for Credit Note Issued and Debit Note Issued
(cherry picked from commit 8f695123cd)
2024-01-23 06:36:09 +00:00
ruthra kumar
fecab1338e refactor(test): payments to invoice with -ve outstanding
(cherry picked from commit f6e4ac2b62)
2024-01-23 06:36:09 +00:00
ruthra kumar
03040c1c7f refactor(test): ledger entries will be against itself
(cherry picked from commit 0e2fb1188a)
2024-01-23 06:36:08 +00:00
ruthra kumar
43b40d92e5 refactor(test): return invoice will have -ve outstanding
(cherry picked from commit b30c1e1abf)
2024-01-23 06:36:08 +00:00
ruthra kumar
014fcfa611 refactor: remove return_against for cr/dr note filter
(cherry picked from commit 00878707ae)
2024-01-23 06:36:08 +00:00
ruthra kumar
0a6af795c4 refactor: cr notes will post for itself
(cherry picked from commit db76e8a277)
2024-01-23 06:36:08 +00:00
rohitwaghchaure
5be99295da Merge pull request #39491 from frappe/mergify/bp/version-14-hotfix/pr-39488
fix: UOM needs to be whole number not being checked in quotations (backport #39488)
2024-01-23 11:54:47 +05:30
Rohit Waghchaure
55c9cc3f26 fix: UOM needs to be whole number not being checked in quotations
(cherry picked from commit aaf83da3e9)
2024-01-22 11:55:27 +00:00
Gursheen Kaur Anand
be56c1838b Merge pull request #39482 from frappe/mergify/bp/version-14-hotfix/pr-39462
fix: party field in PDF for AP / AR reports (backport #39462)
2024-01-22 17:06:09 +05:30
Gursheen Anand
a19b41d8c8 fix: party field in pdf html
(cherry picked from commit b2d9380596)
2024-01-22 09:51:30 +00:00
Gursheen Kaur Anand
1a46f60633 Merge pull request #39473 from frappe/mergify/bp/version-14-hotfix/pr-39229
fix: use most reliable section reference per report line (backport #39229)
2024-01-21 14:23:34 +05:30
David Arnold
3bdff18467 fix: use most reliable section reference per report line
(cherry picked from commit b5be17c6df)
2024-01-21 08:14:13 +00:00
Gursheen Kaur Anand
5c1bf1f3fa Merge pull request #39470 from frappe/mergify/bp/version-14-hotfix/pr-39424
Revert "fix(minor): financial statements period end date" (backport #39424)
2024-01-20 18:51:59 +05:30
Gursheen Kaur Anand
837fff4533 Revert "fix(minor): financial statements period end date"
(cherry picked from commit 73625a2622)
2024-01-20 12:29:22 +00:00
Nabin Hait
f604798a45 fix: asset module test cases 2024-01-19 16:59:52 +05:30
Gursheen Kaur Anand
f405a6c31d Merge pull request #39450 from frappe/mergify/bp/version-14-hotfix/pr-39449
fix: unallocated amount after taxes and charges (backport #39449)
2024-01-19 10:58:27 +05:30
Nabin Hait
b840eb90eb fix: Asset Depreciation WDV as per Income Tax Act 2024-01-18 19:06:41 +05:30
Gursheen Anand
6895b74ecc fix: linting issue
(cherry picked from commit 99b94af49f)
2024-01-18 10:45:49 +00:00
Gursheen Anand
f7ba7361ca fix: set unallocated amount after base tax
(cherry picked from commit e9bc63aacf)
2024-01-18 10:45:49 +00:00
Frappe PR Bot
9aa1e7444e chore(release): Bumped to Version 14.61.0
# [14.61.0](https://github.com/frappe/erpnext/compare/v14.60.1...v14.61.0) (2024-01-17)

### Bug Fixes

* added indexing to improve performance ([d9f7070](d9f7070f92))
* added item group in stock reco ([c69a59c](c69a59c3c6))
* broken dimension filters in Sales/Purchase register ([298cdf5](298cdf5f0e))
* Cancel asset capitalisation record on cancellation of asset and vice-versa ([1dff960](1dff96057c))
* circular dependency error while deleting QC ([dfcb746](dfcb746774))
* composite asset capitalization using asset components ([ac6020a](ac6020a940))
* consider all years in holiday list ([3180266](3180266150))
* date in master document for dictionary condition ([670d615](670d61547f))
* empty category in Plaid ([1acaa20](1acaa20ee1))
* ignore cancelled payments in Sales/Purchase Register ([36b8e97](36b8e972f1))
* incorrect active serial nos due to backdated transactions ([1a26c70](1a26c70df2))
* incorrect percentage received in purchase invoice ([453700d](453700d0ab))
* incorrect sql error if account name has '%' ([d0e3458](d0e3458c8c))
* modified date ([e9d2437](e9d2437c7a))
* modified date ([28434d1](28434d101b))
* modified date was not set ([724c934](724c934fbb))
* modified date was not updated ([cb67574](cb6757437e))
* pass accounts as list to query ([a6bc5ca](a6bc5cae90))
* performance issue related to stock entry (backport [#39301](https://github.com/frappe/erpnext/issues/39301)) ([#39302](https://github.com/frappe/erpnext/issues/39302)) ([dc7c9e7](dc7c9e7aff))
* possible typeerror in consolidated report ([9395f75](9395f7535b))
* possible typerror in utils.js ([4ea72f4](4ea72f4b69))
* project filters on Delivery Note and Sales Order ([520cdb6](520cdb6f32))
* project query controller logic ([f2e577b](f2e577bec7))
* reset default after test ([6bd01f2](6bd01f227e))
* resolved merge conflict ([4edb73d](4edb73d398))
* show bill_date and bill_no in Purchase Register ([4b19792](4b197920c1))
* **test:** test case for project query ([98967ed](98967ed584))
* unreconcile Bank Transaction on cancel of payment voucher ([755576b](755576bd78))
* use child table values instead of global min max ([d21fc60](d21fc6055c))
* WDV as per IT Act: calculate yearly amount first and then split it based on months ([3989b97](3989b97579))
* wrong file name ([9d256e1](9d256e131d))

### Features

* provision to select the qty field for Product Page ([#39292](https://github.com/frappe/erpnext/issues/39292)) ([d42db11](d42db1174d))
2024-01-17 17:05:44 +00:00
Deepesh Garg
e74e5875ea Merge pull request #39404 from frappe/version-14-hotfix
chore: release v14
2024-01-17 22:34:31 +05:30
Nabin Hait
05a2fb3011 Merge pull request #39437 from frappe/mergify/bp/version-14-hotfix/pr-39429
fix: composite asset capitalization using asset components (backport #39429)
2024-01-17 22:05:19 +05:30
Nabin Hait
ac6020a940 fix: composite asset capitalization using asset components
(cherry picked from commit 5df40661d2)
2024-01-17 15:35:56 +00:00
Nabin Hait
9bcbd94bf1 Merge pull request #39421 from frappe/mergify/bp/version-14-hotfix/pr-39386
fix: Cancel asset capitalisation record on cancellation of asset and vice-versa (backport #39386)
2024-01-17 21:05:42 +05:30
Nabin Hait
4edb73d398 fix: resolved merge conflict 2024-01-17 17:36:42 +05:30
Nabin Hait
82732f976a Merge pull request #39432 from frappe/revert-39419-mergify/bp/version-14-hotfix/pr-39385
Revert "fix: WDV as per IT Act: calculate yearly amount first and then split it based on months (backport #39385)"
2024-01-17 17:32:38 +05:30
Nabin Hait
8014839795 Revert "fix: WDV as per IT Act: calculate yearly amount first and then split it based on months (backport #39385)" 2024-01-17 17:32:14 +05:30
Nabin Hait
8d1164f652 Merge pull request #39419 from frappe/mergify/bp/version-14-hotfix/pr-39385
fix: WDV as per IT Act: calculate yearly amount first and then split it based on months (backport #39385)
2024-01-17 17:29:32 +05:30
Nabin Hait
1dff96057c fix: Cancel asset capitalisation record on cancellation of asset and vice-versa
(cherry picked from commit efe9f6656f)

# Conflicts:
#	erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
2024-01-17 06:44:52 +00:00
Nabin Hait
3989b97579 fix: WDV as per IT Act: calculate yearly amount first and then split it based on months
(cherry picked from commit 22bd6a54b2)

# Conflicts:
#	erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
2024-01-17 06:44:25 +00:00
Deepesh Garg
bee90e56f5 Merge pull request #39410 from ljain112/fix-pr
fix(Purchase Register): show bill_date and bill_no (#38742)
2024-01-17 11:00:24 +05:30
ruthra kumar
5693979165 Merge pull request #39411 from frappe/mergify/bp/version-14-hotfix/pr-39402
fix: project query controller logic (backport #39402)
2024-01-17 10:57:36 +05:30
ruthra kumar
42c1de640c chore: resolve conflict 2024-01-17 10:36:29 +05:30
ruthra kumar
b35a83ee47 refactor: better ordering of query result
(cherry picked from commit bfe42fdccb)

# Conflicts:
#	erpnext/controllers/queries.py
2024-01-16 10:55:31 +00:00
ruthra kumar
98967ed584 fix(test): test case for project query
(cherry picked from commit 3349dde5e2)
2024-01-16 10:55:30 +00:00
ruthra kumar
f2e577bec7 fix: project query controller logic
(cherry picked from commit 4eefb445a7)

# Conflicts:
#	erpnext/controllers/queries.py
2024-01-16 10:55:30 +00:00
ljain112
4b197920c1 fix: show bill_date and bill_no in Purchase Register 2024-01-16 15:53:53 +05:30
ruthra kumar
f3628c7d1a Merge pull request #39392 from frappe/mergify/bp/version-14-hotfix/pr-39391
fix: possible typerror in utils.js (backport #39391)
2024-01-15 20:49:43 +05:30
ruthra kumar
4ea72f4b69 fix: possible typerror in utils.js
and remove unwanted debugging statements

(cherry picked from commit 60b26ad8b2)
2024-01-15 15:16:44 +00:00
ruthra kumar
46d846bf1a Merge pull request #39136 from frappe/mergify/bp/version-14-hotfix/pr-39125
fix: ignore cancelled payments in Sales/Purchase Register (backport #39125)
2024-01-15 14:49:22 +05:30
ruthra kumar
a6bc5cae90 fix: pass accounts as list to query 2024-01-15 14:10:55 +05:30
mergify[bot]
3caf11472d ci: bump node in release workflow (backport #39377) (backport #39379) (#39381)
ci: bump node in release workflow (backport #39377) (#39379)

* ci: bump node in release workflow

(cherry picked from commit aef87cced7)

# Conflicts:
#	.github/workflows/release.yml

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
(cherry picked from commit 4af3159f62)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-15 12:15:15 +05:30
mergify[bot]
4af3159f62 ci: bump node in release workflow (backport #39377) (#39379)
* ci: bump node in release workflow

(cherry picked from commit aef87cced7)

# Conflicts:
#	.github/workflows/release.yml

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-01-15 12:11:17 +05:30
ruthra kumar
1ca8dd4349 Merge pull request #39374 from frappe/mergify/bp/version-14-hotfix/pr-39371
fix: incorrect sql error if account name has '%' (backport #39371)
2024-01-15 10:41:04 +05:30
ruthra kumar
9550ed84c0 Merge pull request #39105 from frappe/mergify/bp/version-14-hotfix/pr-39097
refactor: flag to control loyalty point creation at invoice level (backport #39097)
2024-01-15 09:59:30 +05:30
ruthra kumar
d0e3458c8c fix: incorrect sql error if account name has '%'
(cherry picked from commit 641c3de0ca)
2024-01-15 04:10:30 +00:00
rohitwaghchaure
55e790cd2f Merge pull request #39368 from frappe/mergify/bp/version-14-hotfix/pr-39224
Update purchase_taxes_and_charges.json label Rate to Tax Rate (backport #39224)
2024-01-14 11:59:59 +05:30
rohitwaghchaure
e9d2437c7a fix: modified date
(cherry picked from commit 6827edb2c5)
2024-01-14 05:57:43 +00:00
rohitwaghchaure
724c934fbb fix: modified date was not set
(cherry picked from commit 566876ae7a)
2024-01-14 05:57:43 +00:00
mahsem
e9af0c6e67 Update purchase_taxes_and_charges.json label Rate to Tax Rate
Change Rate label to existing Tax Rate label so it can be correctly translated in other languages

(cherry picked from commit bd464197c4)
2024-01-14 05:57:42 +00:00
rohitwaghchaure
e0d12a9b98 Merge pull request #39360 from frappe/mergify/bp/version-14-hotfix/pr-39225
Update sales_taxes_and_charges.json (backport #39225)
2024-01-14 11:24:33 +05:30
rohitwaghchaure
2cc756be57 Merge pull request #39364 from frappe/mergify/bp/version-14-hotfix/pr-39359
fix: added item group in stock reco (backport #39359)
2024-01-14 11:24:08 +05:30
rohitwaghchaure
28434d101b fix: modified date 2024-01-14 10:44:18 +05:30
rohitwaghchaure
4d11a9c884 chore: fix conflicts 2024-01-14 10:32:10 +05:30
Rohit Waghchaure
c69a59c3c6 fix: added item group in stock reco
(cherry picked from commit 116ff8241c)

# Conflicts:
#	erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
2024-01-14 04:58:27 +00:00
rohitwaghchaure
cb6757437e fix: modified date was not updated
(cherry picked from commit f567af49a6)
2024-01-14 04:46:29 +00:00
mahsem
664abc6287 Update sales_taxes_and_charges.json
Change Rate label to existing Tax Rate label so it can be correctly translated in other languages

(cherry picked from commit 2b93be1139)
2024-01-14 04:46:29 +00:00
Deepesh Garg
cc6b6e1653 Merge pull request #39328 from frappe/mergify/bp/version-14-hotfix/pr-38974
fix: unreconcile Bank Transaction on cancel of payment voucher (#38974)
2024-01-14 10:14:46 +05:30
rohitwaghchaure
4815655a6b Merge pull request #39324 from frappe/mergify/bp/version-14-hotfix/pr-39305
fix: naming series variable parsing for FY (backport #39305)
2024-01-14 10:12:00 +05:30
rohitwaghchaure
5981b0e4c4 Merge pull request #39098 from RJPvT/patch-5
fix: wrong file name
2024-01-14 10:07:54 +05:30
rohitwaghchaure
bbee70aea5 Merge pull request #39357 from RJPvT/patch-7
fix: empty category in Plaid
2024-01-14 09:42:55 +05:30
rohitwaghchaure
5336cd49c9 chore: fix conflicts 2024-01-14 09:41:48 +05:30
RJPvT
1acaa20ee1 fix: empty category in Plaid 2024-01-13 16:09:11 +01:00
rohitwaghchaure
a7922e1ef1 Merge pull request #39341 from rohitwaghchaure/fixed-recalculate-current-qty-for-serial-nos
fix: incorrect active serial nos due to backdated transactions
2024-01-13 12:25:07 +05:30
Rohit Waghchaure
1a26c70df2 fix: incorrect active serial nos due to backdated transactions 2024-01-13 10:10:02 +05:30
rohitwaghchaure
519bed9b0f Merge pull request #39344 from frappe/mergify/bp/version-14-hotfix/pr-39333
fix: added indexing to improve performance (backport #39333)
2024-01-12 21:19:37 +05:30
rohitwaghchaure
f1e4beb55a Merge pull request #39343 from frappe/mergify/bp/version-14-hotfix/pr-38970
fix: use local attribute range in multiple item variant dialog (backport #38970)
2024-01-12 20:57:06 +05:30
rohitwaghchaure
c2fee6c25d chore: fix conflicts 2024-01-12 20:56:40 +05:30
Rohit Waghchaure
d9f7070f92 fix: added indexing to improve performance
(cherry picked from commit ac81323fec)

# Conflicts:
#	erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py
2024-01-12 13:10:33 +00:00
Gursheen Anand
d21fc6055c fix: use child table values instead of global min max
(cherry picked from commit 43fed29514)
2024-01-12 13:08:33 +00:00
mergify[bot]
7ae1df60be chore: remove share, print and email permissions from Buying Settings (backport #39337) (#39338)
chore: remove share, print and email permissions from Buying Settings

(cherry picked from commit 3c46abca6c)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-01-12 16:48:50 +05:30
rohitwaghchaure
9b85d425fa Merge pull request #39311 from frappe/mergify/bp/version-14-hotfix/pr-39299
fix: circular dependency error on deletion of QC and Stock Entry (backport #39299)
2024-01-11 20:34:34 +05:30
Raffael Meyer
385ed8aa80 Merge pull request #39326 from frappe/mergify/bp/version-14-hotfix/pr-39117
fix: consider all years in holiday list (backport #39117)
2024-01-11 15:04:24 +01:00
barredterra
4998d68564 chore: resolve merge confilcts 2024-01-11 15:04:01 +01:00
barredterra
4a1a4b06dd test: cancel voucher linked to Bank Transaction
(cherry picked from commit 517bedeb7e)
2024-01-11 13:37:32 +00:00
barredterra
755576bd78 fix: unreconcile Bank Transaction on cancel of payment voucher
(cherry picked from commit 0a95b38166)

# Conflicts:
#	erpnext/accounts/doctype/bank_transaction/bank_transaction.py
#	erpnext/accounts/doctype/journal_entry/journal_entry.js
2024-01-11 13:37:32 +00:00
barredterra
f3d8d273f4 test: improve test for local holidays
(cherry picked from commit 60329ade9e)
2024-01-11 13:28:41 +00:00
barredterra
3180266150 fix: consider all years in holiday list
(cherry picked from commit 300aaa39fe)
2024-01-11 13:28:40 +00:00
Gursheen Anand
6bd01f227e fix: reset default after test
(cherry picked from commit 813b7a96fb)
2024-01-11 13:26:02 +00:00
Gursheen Anand
d95b14d5b8 test: naming series variable parsing
(cherry picked from commit bbdf98a8f0)
2024-01-11 13:26:02 +00:00
Gursheen Anand
670d61547f fix: date in master document for dictionary condition
(cherry picked from commit d96a777edd)

# Conflicts:
#	erpnext/accounts/utils.py
2024-01-11 13:26:01 +00:00
ruthra kumar
db3cb5c994 Merge pull request #39322 from frappe/mergify/bp/version-14-hotfix/pr-39212
fix: project filters on Delivery Note and Sales Order (backport #39212)
2024-01-11 18:06:55 +05:30
ruthra kumar
520cdb6f32 fix: project filters on Delivery Note and Sales Order
(cherry picked from commit 9ba6ff67d5)
2024-01-11 12:34:44 +00:00
ruthra kumar
e5b90c8ec5 Merge pull request #39321 from frappe/mergify/bp/version-14-hotfix/pr-39320
fix: possible typeerror in consolidated report (backport #39320)
2024-01-11 17:13:00 +05:30
ruthra kumar
faa312e680 Merge pull request #39318 from frappe/mergify/bp/version-14-hotfix/pr-39317
fix: broken dimension filters in Sales/Purchase register (backport #39317)
2024-01-11 17:12:48 +05:30
ruthra kumar
9395f7535b fix: possible typeerror in consolidated report
(cherry picked from commit 268731aec4)
2024-01-11 16:59:32 +05:30
ruthra kumar
298cdf5f0e fix: broken dimension filters in Sales/Purchase register
(cherry picked from commit 7b3f9386d7)
2024-01-11 11:20:42 +00:00
rohitwaghchaure
531f15b5d8 Merge pull request #39314 from frappe/mergify/bp/version-14-hotfix/pr-39215
fix: incorrect percentage received in purchase invoice (backport #39215)
2024-01-11 15:14:52 +05:30
rohitwaghchaure
daf0e435e2 chore: fix conflicts 2024-01-11 14:53:21 +05:30
Rohit Waghchaure
453700d0ab fix: incorrect percentage received in purchase invoice
(cherry picked from commit 8d2c78867e)
2024-01-11 09:21:23 +00:00
Rohit Waghchaure
dfcb746774 fix: circular dependency error while deleting QC
(cherry picked from commit 7cc324e31e)

# Conflicts:
#	erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
2024-01-11 09:20:08 +00:00
Frappe PR Bot
5ae050ecd4 chore(release): Bumped to Version 14.60.1
## [14.60.1](https://github.com/frappe/erpnext/compare/v14.60.0...v14.60.1) (2024-01-10)

### Bug Fixes

* performance issue related to stock entry (backport [#39301](https://github.com/frappe/erpnext/issues/39301)) ([#39302](https://github.com/frappe/erpnext/issues/39302)) ([49d914b](49d914bdb6))
2024-01-10 16:55:11 +00:00
rohitwaghchaure
d3cc0c9aea Merge pull request #39304 from frappe/mergify/bp/version-14/pr-39302
fix: performance issue related to stock entry (backport #39301) (backport #39302)
2024-01-10 22:23:58 +05:30
mergify[bot]
49d914bdb6 fix: performance issue related to stock entry (backport #39301) (#39302)
fix: performance issue related to stock entry (#39301)

(cherry picked from commit c67b0a3a64)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit dc7c9e7aff)
2024-01-10 16:53:05 +00:00
mergify[bot]
dc7c9e7aff fix: performance issue related to stock entry (backport #39301) (#39302)
fix: performance issue related to stock entry (#39301)

(cherry picked from commit c67b0a3a64)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-10 22:22:16 +05:30
Frappe PR Bot
5fd68f7204 chore(release): Bumped to Version 14.60.0
# [14.60.0](https://github.com/frappe/erpnext/compare/v14.59.0...v14.60.0) (2024-01-10)

### Features

* provision to select the qty field for Product Page (backport [#39292](https://github.com/frappe/erpnext/issues/39292)) ([#39300](https://github.com/frappe/erpnext/issues/39300)) ([760af49](760af497ca))
2024-01-10 16:24:39 +00:00
mergify[bot]
760af497ca feat: provision to select the qty field for Product Page (backport #39292) (#39300)
feat: provision to select the qty field for Product Page (#39292)

feat: provision to select the qty field to be shown as `In Stock` in product page
(cherry picked from commit d42db1174d)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-01-10 21:51:34 +05:30
s-aga-r
d42db1174d feat: provision to select the qty field for Product Page (#39292)
feat: provision to select the qty field to be shown as `In Stock` in product page
2024-01-10 21:31:03 +05:30
Frappe PR Bot
2e6f1378f1 chore(release): Bumped to Version 14.59.0
# [14.59.0](https://github.com/frappe/erpnext/compare/v14.58.1...v14.59.0) (2024-01-10)

### Bug Fixes

* add read permission to Buying Settings (backport [#39158](https://github.com/frappe/erpnext/issues/39158)) ([#39257](https://github.com/frappe/erpnext/issues/39257)) ([a15ad80](a15ad804d0))
* asset WDV depreciation calc according to IT act ([1cbe1e8](1cbe1e894d))
* BOM replace tool does not update exploded items of root (backport [#39244](https://github.com/frappe/erpnext/issues/39244)) ([#39249](https://github.com/frappe/erpnext/issues/39249)) ([c2eeeec](c2eeeecac8))
* don't set rate for non-stock item in Internal Transfer (backport [#39140](https://github.com/frappe/erpnext/issues/39140)) ([#39168](https://github.com/frappe/erpnext/issues/39168)) ([06d193a](06d193ad87))
* Duplicate Closing Stock Balance (backport [#39262](https://github.com/frappe/erpnext/issues/39262)) ([#39263](https://github.com/frappe/erpnext/issues/39263)) ([a3146c3](a3146c39dd))
* **Employee:** treeview ([#39126](https://github.com/frappe/erpnext/issues/39126)) ([080a742](080a742725))
* FG Item incorrect qty in the work order (backport [#39200](https://github.com/frappe/erpnext/issues/39200)) ([#39210](https://github.com/frappe/erpnext/issues/39210)) ([5e517cf](5e517cfc77))
* Ignore asset qty and status validation while cancelling LCV ([952cee3](952cee3d6a))
* Ignore UP on "allowed to transact with" (backport [#39103](https://github.com/frappe/erpnext/issues/39103)) ([#39104](https://github.com/frappe/erpnext/issues/39104)) ([7d64df0](7d64df05bc))
* improved validation message ([580e9f6](580e9f6e10))
* incorrect indicator title for portal sales order (backport [#39247](https://github.com/frappe/erpnext/issues/39247)) ([#39254](https://github.com/frappe/erpnext/issues/39254)) ([8c496fb](8c496fbf2b))
* inventory dimension negative stock validation (backport [#39149](https://github.com/frappe/erpnext/issues/39149)) ([#39150](https://github.com/frappe/erpnext/issues/39150)) ([2866f7c](2866f7c441))
* possible typeerror on transaction.js ([8fe346a](8fe346aef8))
* Purchase date and amount is not mandatory for composite asset creation ([0f6477a](0f6477a253))
* remove global _("translation") calls (backport [#32828](https://github.com/frappe/erpnext/issues/32828)) ([#39231](https://github.com/frappe/erpnext/issues/39231)) ([ee7474b](ee7474ba20))
* resolved conflict ([b529883](b529883897))
* set `First Name` in Supplier Contact ([69c460c](69c460c756))
* Set asset purchase amount based on qty and valuation_rate ([41e3843](41e384326e))
* set parent doctype on chart (backport [#39286](https://github.com/frappe/erpnext/issues/39286)) ([#39287](https://github.com/frappe/erpnext/issues/39287)) ([a2a8558](a2a8558677))
* Show maintain-stock and is-fixed-asset checkbox in item quick entry dialog ([7b4b630](7b4b630b2c))
* Show timesheet table after fetching data from timesheet (backport [#39275](https://github.com/frappe/erpnext/issues/39275)) ([#39280](https://github.com/frappe/erpnext/issues/39280)) ([1cc887a](1cc887a997))
* skip rate validation for return `DN Items` with `Moving Average` valuation (backport [#39242](https://github.com/frappe/erpnext/issues/39242)) ([#39265](https://github.com/frappe/erpnext/issues/39265)) ([f42e93b](f42e93bf6c))
* total allocated percentage for sales team issue ([71f9b7f](71f9b7f675))
* TypeError is pricing rules (backport [#39252](https://github.com/frappe/erpnext/issues/39252)) ([#39259](https://github.com/frappe/erpnext/issues/39259)) ([36ba33c](36ba33c500))
* typerror on multi select dialog ([71ecf08](71ecf081c3))
* update Maintenance Schedule status on Maintenance Visit submit (backport [#39167](https://github.com/frappe/erpnext/issues/39167)) ([#39185](https://github.com/frappe/erpnext/issues/39185)) ([2ea2146](2ea2146b34))
* **UX:** dont override framework's permission check messages (backport [#39118](https://github.com/frappe/erpnext/issues/39118)) ([#39119](https://github.com/frappe/erpnext/issues/39119)) ([26ae708](26ae708d6d))

### Features

* Copy project_name, from_time, to_time from timesheet details to sales invoice ([#33726](https://github.com/frappe/erpnext/issues/33726)) ([d0e1162](d0e1162c96))
2024-01-10 10:35:32 +00:00
Deepesh Garg
2d0e1830a9 Merge pull request #39246 from frappe/version-14-hotfix
chore: release v14
2024-01-10 16:04:20 +05:30
mergify[bot]
1cc887a997 fix: Show timesheet table after fetching data from timesheet (backport #39275) (#39280)
fix: Show timesheet table after fetching data from timesheet

(cherry picked from commit e1ba5878a3)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-01-10 12:33:28 +05:30
mergify[bot]
ee7474ba20 fix: remove global _("translation") calls (backport #32828) (#39231)
fix: remove global _("translation") calls (#32828)

This is not how it works. Translations are dynamic based on language
sets during request (using header, user's preferences etc)

Calling them on global variables makes no sense.

Ref: https://github.com/frappe/frappe/pull/18733
(cherry picked from commit 75983ce809)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2024-01-10 12:31:59 +05:30
mergify[bot]
a2a8558677 fix: set parent doctype on chart (backport #39286) (#39287)
fix: set parent doctype on chart (#39286)

(cherry picked from commit 38c5ecf007)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2024-01-10 12:27:13 +05:30
Nabin Hait
cc07213faa Merge pull request #39189 from frappe/mergify/bp/version-14-hotfix/pr-39163
fix: Purchase information is not required for creating a composite asset (backport #39163)
2024-01-10 12:15:43 +05:30
Nabin Hait
f4e3f0d656 Merge pull request #39278 from frappe/mergify/bp/version-14-hotfix/pr-39191
fix: Ignore asset qty and status validation while cancelling LCV (backport #39191)
2024-01-10 12:14:36 +05:30
Nabin Hait
0379ea5086 Merge pull request #39277 from frappe/mergify/bp/version-14-hotfix/pr-39238
fix: Set asset purchase amount based on qty and valuation_rate (backport #39238)
2024-01-10 12:14:24 +05:30
Nabin Hait
65005d8630 Merge pull request #39050 from anandbaburajan/asset_wdv_india
fix: asset WDV depreciation calc according to IT act [v14]
2024-01-10 12:13:53 +05:30
Nabin Hait
3f3fc8214d Merge pull request #39217 from frappe/mergify/bp/version-14-hotfix/pr-39214
fix: Show maintain-stock and is-fixed-asset checkbox in item quick entry dialog (backport #39214)
2024-01-10 12:12:24 +05:30
Nabin Hait
b529883897 fix: resolved conflict 2024-01-10 12:01:32 +05:30
ruthra kumar
3d621d47a6 Merge pull request #39283 from frappe/mergify/bp/version-14-hotfix/pr-39282
fix: possible typeerror on transaction.js (backport #39282)
2024-01-10 12:00:50 +05:30
ruthra kumar
8fe346aef8 fix: possible typeerror on transaction.js
(cherry picked from commit 9f27ac142b)
2024-01-10 06:24:46 +00:00
Nabin Hait
952cee3d6a fix: Ignore asset qty and status validation while cancelling LCV
(cherry picked from commit e9d36242ce)
2024-01-10 06:04:10 +00:00
Nabin Hait
41e384326e fix: Set asset purchase amount based on qty and valuation_rate
(cherry picked from commit 135e19d0aa)
2024-01-10 06:03:03 +00:00
mergify[bot]
f42e93bf6c fix: skip rate validation for return DN Items with Moving Average valuation (backport #39242) (#39265)
* fix: skip rate validation for return `DN Items` with `Moving Average` valuation

(cherry picked from commit e0ad52b500)

# Conflicts:
#	erpnext/controllers/sales_and_purchase_return.py

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-01-10 10:53:09 +05:30
mergify[bot]
a3146c39dd fix: Duplicate Closing Stock Balance (backport #39262) (#39263)
fix: Duplicate Closing Stock Balance

(cherry picked from commit b15795392b)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-01-09 20:37:49 +05:30
mergify[bot]
a15ad804d0 fix: add read permission to Buying Settings (backport #39158) (#39257)
* fix: add read permission to Buying Settings

(cherry picked from commit e05bf9d32a)

# Conflicts:
#	erpnext/buying/doctype/buying_settings/buying_settings.json

* chore: resolve merge conflicts

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2024-01-09 19:09:06 +05:30
mergify[bot]
8c496fbf2b fix: incorrect indicator title for portal sales order (backport #39247) (#39254)
fix: incorrect indicator title for portal sales order (#39247)

(cherry picked from commit 2d2ff7cf52)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-09 17:43:18 +05:30
mergify[bot]
36ba33c500 fix: TypeError is pricing rules (backport #39252) (#39259)
fix: TypeError is pricing rules (#39252)

(cherry picked from commit 274c65c451)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-09 17:42:04 +05:30
mergify[bot]
c2eeeecac8 fix: BOM replace tool does not update exploded items of root (backport #39244) (#39249)
fix: BOM replace tool does not update exploded items of root (#39244)

(cherry picked from commit 5e0d017497)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-09 15:59:24 +05:30
ruthra kumar
c9d4c41db3 Merge pull request #39240 from frappe/mergify/bp/version-14-hotfix/pr-39237
fix: sales team commission overallocation on fetching items from multiple quotations to Sales Order (backport #39237)
2024-01-09 14:40:15 +05:30
ruthra kumar
71f9b7f675 fix: total allocated percentage for sales team issue
(cherry picked from commit b498094a97)
2024-01-09 07:38:57 +00:00
ruthra kumar
0ebd133eda Merge pull request #39236 from frappe/mergify/bp/version-14-hotfix/pr-33726
feat: Copy project_name, from_time, to_time from timesheet details to sales invoice. (backport #33726)
2024-01-09 12:43:27 +05:30
Kevin Shenk
d0e1162c96 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
(cherry picked from commit e4bceaaf66)
2024-01-09 06:33:24 +00:00
Nabin Hait
7b4b630b2c fix: Show maintain-stock and is-fixed-asset checkbox in item quick entry dialog
(cherry picked from commit c14986f9e6)
2024-01-08 16:07:25 +00:00
mergify[bot]
5e517cfc77 fix: FG Item incorrect qty in the work order (backport #39200) (#39210)
fix: FG Item incorrect qty in the work order (#39200)

(cherry picked from commit 466625213b)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-08 17:43:36 +05:30
mergify[bot]
2ea2146b34 fix: update Maintenance Schedule status on Maintenance Visit submit (backport #39167) (#39185)
* fix: make `Sales Person` non-mandatory

(cherry picked from commit 4d56f725fe)

* fix: update Maintenance Schedule status on Maintenance Visit submit

(cherry picked from commit cd293a5173)

# Conflicts:
#	erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
#	erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.py

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-01-08 16:27:19 +05:30
s-aga-r
856926d664 Merge pull request #39184 from s-aga-r/FIX-7838
fix: set `First Name` in Supplier Contact
2024-01-08 15:36:07 +05:30
Nabin Hait
0f6477a253 fix: Purchase date and amount is not mandatory for composite asset creation
(cherry picked from commit c34f09c503)

# Conflicts:
#	erpnext/assets/doctype/asset/asset.py
2024-01-08 06:52:33 +00:00
Nabin Hait
580e9f6e10 fix: improved validation message
(cherry picked from commit fe43dab4d7)
2024-01-08 06:52:32 +00:00
s-aga-r
69c460c756 fix: set First Name in Supplier Contact 2024-01-08 11:47:00 +05:30
mergify[bot]
06d193ad87 fix: don't set rate for non-stock item in Internal Transfer (backport #39140) (#39168)
* fix: don't set rate for non-stock item in Internal Transfer

(cherry picked from commit e1b0fffd0c)

* test: internal transfer for non-stock item

(cherry picked from commit 57b6a98703)

# Conflicts:
#	erpnext/stock/doctype/delivery_note/test_delivery_note.py

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2024-01-08 11:44:52 +05:30
ruthra kumar
f6b10f3d84 Merge pull request #39180 from frappe/mergify/bp/version-14-hotfix/pr-39159
refactor: prevent permission error by always processing bulk transaction in background (backport #39159)
2024-01-08 10:30:25 +05:30
ruthra kumar
f236a2c081 refactor: prevent permissions by always processing in background
(cherry picked from commit 15dc5c7e99)
2024-01-08 04:42:49 +00:00
mergify[bot]
2866f7c441 fix: inventory dimension negative stock validation (backport #39149) (#39150)
fix: inventory dimension negative stock validation (#39149)

(cherry picked from commit bae7c64964)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-01-05 05:03:22 +05:30
Raffael Meyer
948045ea32 Merge pull request #39147 from frappe/mergify/bp/version-14-hotfix/pr-39126
fix(Employee): treeview (backport #39126)
2024-01-04 15:23:53 +01:00
Raffael Meyer
080a742725 fix(Employee): treeview (#39126)
(cherry picked from commit e912e9597d)
2024-01-04 12:34:48 +00:00
ruthra kumar
36b8e972f1 fix: ignore cancelled payments in Sales/Purchase Register
(cherry picked from commit 0f1be03faf)
2024-01-04 16:24:07 +05:30
Frappe PR Bot
feaa16d748 chore(release): Bumped to Version 14.58.1
## [14.58.1](https://github.com/frappe/erpnext/compare/v14.58.0...v14.58.1) (2024-01-03)

### Bug Fixes

* typerror on multi select dialog ([43cc7e4](43cc7e4a59))
2024-01-03 15:37:55 +00:00
ruthra kumar
6ca29a93bf Merge pull request #39123 from frappe/mergify/bp/version-14/pr-39116
fix: typerror on multi select dialog (backport #39116)
2024-01-03 21:06:48 +05:30
ruthra kumar
20aac93731 Merge pull request #39121 from frappe/mergify/bp/version-14-hotfix/pr-39116
fix: typerror on multi select dialog (backport #39116)
2024-01-03 21:05:48 +05:30
ruthra kumar
43cc7e4a59 fix: typerror on multi select dialog
(cherry picked from commit 7da9ffa3bd)
2024-01-03 15:26:11 +00:00
ruthra kumar
71ecf081c3 fix: typerror on multi select dialog
(cherry picked from commit 7da9ffa3bd)
2024-01-03 15:24:49 +00:00
mergify[bot]
26ae708d6d fix(UX): dont override framework's permission check messages (backport #39118) (#39119)
fix(UX): dont override framework's permission check messages (#39118)

(cherry picked from commit e84c9f7c51)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2024-01-03 20:32:55 +05:30
ruthra kumar
9277b02557 refactor: flag to control loyalty point creation at invoice level
(cherry picked from commit 1bc74bde29)
2024-01-03 13:39:32 +05:30
mergify[bot]
7d64df05bc fix: Ignore UP on "allowed to transact with" (backport #39103) (#39104)
fix: Ignore UP on "allowed to transact with" (#39103)

If a customer is allowed to transact with some company it usually
doesn't imply that customer is somehow "linked with" that company.

(cherry picked from commit 6401908f41)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2024-01-03 13:00:44 +05:30
Frappe PR Bot
b6a36270b2 chore(release): Bumped to Version 14.58.0
# [14.58.0](https://github.com/frappe/erpnext/compare/v14.57.0...v14.58.0) (2024-01-03)

### Bug Fixes

* Add missing french translations (backport [#38368](https://github.com/frappe/erpnext/issues/38368)) ([#38513](https://github.com/frappe/erpnext/issues/38513)) ([2c90ee2](2c90ee23f9))
* **DX:** capture tracebacks with context (backport [#39060](https://github.com/frappe/erpnext/issues/39060)) ([#39062](https://github.com/frappe/erpnext/issues/39062)) ([fe9acc8](fe9acc898e))
* **Hierarchy Chart:** check if company is set before loading children ([#38985](https://github.com/frappe/erpnext/issues/38985)) ([e4d6df3](e4d6df39ff))
* issue occured when creating supplier with contact details (backport [#38147](https://github.com/frappe/erpnext/issues/38147)) ([#39046](https://github.com/frappe/erpnext/issues/39046)) ([81ef7b4](81ef7b4c00))
* Opening balance in bank reconciliation tool ([#38977](https://github.com/frappe/erpnext/issues/38977)) ([a2cba1b](a2cba1bf23))
* remove bad defaults (backport [#38986](https://github.com/frappe/erpnext/issues/38986)) ([#38987](https://github.com/frappe/erpnext/issues/38987)) ([29d383a](29d383ad8b))
* select options should dynamically load dimensions ([bfc94cf](bfc94cf284))
* take quantity into account when setting asset's gross purchase amt (backport [#39056](https://github.com/frappe/erpnext/issues/39056)) ([#39057](https://github.com/frappe/erpnext/issues/39057)) ([b8dce3e](b8dce3eeac))
* undefined error in Budget Variance and Profitability report ([cad15cd](cad15cdec2))
* use `Stock Qty` while getting `POS Reserved Qty` (backport [#38962](https://github.com/frappe/erpnext/issues/38962)) ([#38982](https://github.com/frappe/erpnext/issues/38982)) ([7ad42ec](7ad42ec957))
* Validate account in Sales/Purchase Taxes and Charges Template ([#39013](https://github.com/frappe/erpnext/issues/39013)) ([c0b5980](c0b598074c))
* work order with multi level, fetch operting cost from sub-assembly (backport [#38992](https://github.com/frappe/erpnext/issues/38992)) ([#39027](https://github.com/frappe/erpnext/issues/39027)) ([88e5c9e](88e5c9e61b))

### Features

* Merge taxes from mapped docs ([#38346](https://github.com/frappe/erpnext/issues/38346)) ([c74e6aa](c74e6aaebb))
* Show Ledger view for Purchase & Sales Register ([#38801](https://github.com/frappe/erpnext/issues/38801)) ([04fb215](04fb215ff5))

### Performance Improvements

* index item_code in bom explosion item (backport [#39085](https://github.com/frappe/erpnext/issues/39085)) ([#39087](https://github.com/frappe/erpnext/issues/39087)) ([558861b](558861b634))
2024-01-03 06:01:14 +00:00
Deepesh Garg
fa442ca788 Merge pull request #39091 from frappe/version-14-hotfix
chore: release v14
2024-01-03 11:29:59 +05:30
RJPvT
9d256e131d fix: wrong file name
file name is actually reversed.

In [1]: from premailer import Premailer
   ...:
   ...: from frappe.utils.jinja_globals import bundled_asset
   ...:
   ...: # get email css files from hooks
   ...: css_files = frappe.get_hooks("email_css")
   ...: css_files = [frappe.utils.jinja_globals.bundled_asset(path) for path in
   ...: css_files]
   ...: css_files = [path.lstrip("/") for path in css_files]

In [2]: css_files
Out[2]:
['assets/frappe/dist/css/email.bundle.ELTO33N3.css',
 'email_erpnext.bundle.css',
 'assets/css/email.css']

In [3]: css_files = [css_file for css_file in css_files if os.path.exists(os.pat
   ...: h.abspath(css_file))]

In [4]: css_files
Out[4]: ['assets/frappe/dist/css/email.bundle.ELTO33N3.css', 'assets/css/email.css']
2024-01-02 22:01:51 +01:00
mergify[bot]
558861b634 perf: index item_code in bom explosion item (backport #39085) (#39087)
perf: index item_code in bom explosion item (#39085)

(cherry picked from commit 739434b727)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2024-01-02 14:43:23 +05:30
mergify[bot]
2c90ee23f9 fix: Add missing french translations (backport #38368) (#38513)
* fix: Add missing french translations (#38368)

fix: Add missing french translation

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
(cherry picked from commit a1e0197a8b)

# Conflicts:
#	erpnext/translations/fr.csv

* chore: resolve merge conflicts

---------

Co-authored-by: noec764 <58433943+noec764@users.noreply.github.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2024-01-02 13:51:43 +05:30
ruthra kumar
78771cd72f Merge pull request #39071 from frappe/mergify/bp/version-14-hotfix/pr-39067
fix: undefined error in Budget Variance and Profitability report (backport #39067)
2024-01-01 17:49:03 +05:30
ruthra kumar
bfc94cf284 fix: select options should dynamically load dimensions
(cherry picked from commit 1a9e091d12)
2024-01-01 17:41:15 +05:30
ruthra kumar
cad15cdec2 fix: undefined error in Budget Variance and Profitability report
'Budget' and 'Budget Account' doesn't have support for dynamic
dimension. It only supports hard-coded ones -  Project and Cost Center

(cherry picked from commit 92bc962f60)

# Conflicts:
#	erpnext/accounts/report/profitability_analysis/profitability_analysis.js
2024-01-01 12:08:03 +00:00
ruthra kumar
96dfcd7149 Merge pull request #39068 from frappe/mergify/bp/version-14-hotfix/pr-39023
refactor(perf): replace account subquery with 'in' condition (backport #39023)
2024-01-01 16:29:07 +05:30
ruthra kumar
c7d17d21e8 refactor(perf): replace account subquery with 'in' condition
(cherry picked from commit a517125d64)
2024-01-01 10:13:50 +00:00
mergify[bot]
fe9acc898e fix(DX): capture tracebacks with context (backport #39060) (#39062)
* fix(DX): capture tracebacks with context (#39060)

(cherry picked from commit 510fdf7bf6)

# Conflicts:
#	erpnext/manufacturing/doctype/bom_creator/bom_creator.py

* Delete erpnext/manufacturing/doctype/bom_creator/bom_creator.py

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2024-01-01 15:38:22 +05:30
mergify[bot]
b8dce3eeac fix: take quantity into account when setting asset's gross purchase amt (backport #39056) (#39057)
fix: take quantity into account when setting asset's gross purchase amt (#39056)

fix: take quantity into account when setting asset's gross purchase amount
(cherry picked from commit 0346f47c1d)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2024-01-01 11:27:46 +05:30
anandbaburajan
1cbe1e894d fix: asset WDV depreciation calc according to IT act 2023-12-31 23:34:15 +05:30
mergify[bot]
81ef7b4c00 fix: issue occured when creating supplier with contact details (backport #38147) (#39046)
* fix: issue occured when creating supplier with contact details

(cherry picked from commit 7842c9fba8)

# Conflicts:
#	erpnext/selling/doctype/customer/customer.py

* fix: Suppier name was not taken when creating address from supplier

(cherry picked from commit 545ef3c234)

* chore: fix conflicts

* chore: fix linter issues

---------

Co-authored-by: kunhi <kunhimohamed6@gmail.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-12-31 18:06:47 +05:30
mergify[bot]
88e5c9e61b fix: work order with multi level, fetch operting cost from sub-assembly (backport #38992) (#39027)
* fix: work order with multi level, fetch operting cost from sub-assembly (#38992)

(cherry picked from commit 70abedc57a)

# Conflicts:
#	erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py
#	erpnext/manufacturing/doctype/work_order/test_work_order.py

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-12-30 11:14:23 +05:30
mergify[bot]
c0b598074c fix: Validate account in Sales/Purchase Taxes and Charges Template (#39013)
fix: Validate account in Sales/Purchase Taxes and Charges Template (#39013)

(cherry picked from commit cd37fd790b)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-12-29 16:15:45 +05:30
Gursheen Kaur Anand
04fb215ff5 feat: Show Ledger view for Purchase & Sales Register (#38801)
* feat: fetch payments and journals

* refactor: use single qb query for PE and PI

* feat: fetch PE along with SI

* refactor: move repeating code to common controller

* fix: fetch cost center for PE

* feat: fetch JV with PE

* fix: validate party filter for fetching payments

* fix: linting issues

* refactor: use qb to fetch PE JV and Inv

* refactor: move fn to fetch advance taxes to utils & use qb

* fix: filtering through accounting dimensions

* chore: remove debugging print statements

* fix: modify rows and columns for ledger view

* refactor: filter accounting dimensions using qb

* fix: show additional table cols from india compliance api call

* chore: fix typo

* test: purchase register and ledger view

* fix: filter by party in opening row calculation

* fix: exclude cancelled gl entries for opening balance

* chore: change column format for report

* fix: check gl entry status using is_cancelled

* fix: running balance after sorting

* fix: gst itemised registers for india compliance api call

* fix: additional query cols for gst itemised registers

* fix: additional query cols for sales register

* fix: PE in sales register

* fix: tax accounts in sales register

* fix: Sales/Purchase register showing duplicate records

* fix: avoid fetching advance account for party

* refactor: remove unnecessary condition

* fix: remove checking for advance accounts in payments

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-12-29 13:02:17 +05:30
mergify[bot]
a2cba1bf23 fix: Opening balance in bank reconciliation tool (#38977)
fix: Opening balance in bank reconciliation tool (#38977)

(cherry picked from commit bbee9b5637)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-12-28 17:53:27 +05:30
mergify[bot]
29d383ad8b fix: remove bad defaults (backport #38986) (#38987)
fix: remove bad defaults (#38986)

Child tables can't have a default.

(cherry picked from commit b71b0d5997)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-12-28 14:22:23 +05:30
Rucha Mahabal
e4d6df39ff fix(Hierarchy Chart): check if company is set before loading children (#38985)
* fix(Org Chart): check if company is set before loading children

* refactor: avoid assigning undefined values, use empty strings, null instead

* fix: returning to org chart view from employee master does not render chart

- the check for company field is truthy from the employee doctype, so it does not render company field again. Explicitly check for company field on the same page

* fix(Org Chart Mobile): check if company is set before loading children
2023-12-28 12:54:31 +05:30
mergify[bot]
7ad42ec957 fix: use Stock Qty while getting POS Reserved Qty (backport #38962) (#38982)
fix: use `Stock Qty` while getting `POS Reserved Qty`

(cherry picked from commit 7223106417)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-12-28 11:59:47 +05:30
mergify[bot]
c74e6aaebb feat: Merge taxes from mapped docs (#38346)
* feat: Merge taxes from mapped docs (#38346)

* feat: Merge taxes from mapped docs

* chore: ci failures

(cherry picked from commit 9b1c22250f)

* chore: ci failures

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-12-28 10:03:26 +05:30
VihangT
92d61eb19e fix : correct logic for overlap error (#38798)
* fix : correct logic for overlap error

* fix : 

Updated as per lintels

* fix : 

changes as per linters

* fix : correct logic for overlap error

fixing overlap error logic with taking care of sequential time job cards in overlap job card list

Added Provision if time_logs list is empty
2023-12-27 16:11:14 +05:30
Frappe PR Bot
6e479f918d chore(release): Bumped to Version 14.57.0
# [14.57.0](https://github.com/frappe/erpnext/compare/v14.56.0...v14.57.0) (2023-12-27)

### Bug Fixes

* allow to set rate manually for service item in BOM ([#38880](https://github.com/frappe/erpnext/issues/38880)) ([c2f692a](c2f692a4e4))
* display all item rate stop messages ([226d0e0](226d0e0196))
* do not reset the basic rate for the material receipt stock entry ([#38896](https://github.com/frappe/erpnext/issues/38896)) ([98bfcc4](98bfcc4c75))
* first name error on customer creation ([#38927](https://github.com/frappe/erpnext/issues/38927)) ([28f052d](28f052d586))
* german translations for Lost Quotations ([#38435](https://github.com/frappe/erpnext/issues/38435)) ([81b5e6c](81b5e6c5f1))
* groups for current accounts in German CoAs ([39aa36e](39aa36e44b))
* incorrect price list in customer-wise item price report ([7577706](7577706a9d))
* min order qty optional in production plan (backport [#38956](https://github.com/frappe/erpnext/issues/38956)) ([#38957](https://github.com/frappe/erpnext/issues/38957)) ([f6f2712](f6f2712db0))
* remove rows with zero consumed qty ([f2f4100](f2f410093a))
* reposting not fixing valuation rate for sales return using movin… ([#38895](https://github.com/frappe/erpnext/issues/38895)) ([3a668bb](3a668bbe96))
* reset the incoming rate on changing of the warehouse ([#38909](https://github.com/frappe/erpnext/issues/38909)) ([2770ca1](2770ca1b65))
* skip jvs against bank accounts ([98198d3](98198d35ea))
* **test:** expect account currency when party account is specified. ([30cb218](30cb218638))
* unset discount amount from coupon discounts ([87e8dd0](87e8dd0d63))
* use party account currency when party account is specified ([d9e0d55](d9e0d55b88))
* **ux:** make PR and PI Item rate field readonly based on `Maintain Same Rate` (backport [#38942](https://github.com/frappe/erpnext/issues/38942)) ([#38943](https://github.com/frappe/erpnext/issues/38943)) ([1eee203](1eee203f7f))

### Features

* in_party_currency option for AR/AP reports ([3c717db](3c717db3fd))
* total_asset_cost field (backport [#38879](https://github.com/frappe/erpnext/issues/38879)) ([#38886](https://github.com/frappe/erpnext/issues/38886)) ([8169c7d](8169c7de0d))

### Performance Improvements

* Drop unused/duplicate/sub-optimal indexes (backport [#38884](https://github.com/frappe/erpnext/issues/38884)) ([#38912](https://github.com/frappe/erpnext/issues/38912)) ([b254a72](b254a72d41))
* use estimated rows instead of actual rows (backport [#38830](https://github.com/frappe/erpnext/issues/38830)) ([#38875](https://github.com/frappe/erpnext/issues/38875)) ([4d1ccd9](4d1ccd9e27))
2023-12-27 08:40:26 +00:00
rohitwaghchaure
8e8d0ad9aa Merge pull request #38953 from frappe/version-14-hotfix
chore: release v14
2023-12-27 14:09:17 +05:30
rohitwaghchaure
400c78c600 Merge branch 'version-14' into version-14-hotfix 2023-12-27 10:24:01 +05:30
mergify[bot]
f6f2712db0 fix: min order qty optional in production plan (backport #38956) (#38957)
* fix: min order qty optional in production plan (#38956)

* fix: min order qty optional in production plan

* fix: test cases

(cherry picked from commit b09c9354fb)

# Conflicts:
#	erpnext/manufacturing/doctype/bom/bom.json
#	erpnext/manufacturing/doctype/production_plan/production_plan.py

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-12-26 21:59:09 +05:30
mergify[bot]
1eee203f7f fix(ux): make PR and PI Item rate field readonly based on Maintain Same Rate (backport #38942) (#38943)
* fix(ux): make PI Item rate field editable

(cherry picked from commit eb5bb9f9a9)

* fix(ux): make PI Item rate field readonly based on `Maintain Same Rate`

(cherry picked from commit cb9114442b)

* fix(ux): make PR Item rate field readonly based on `Maintain Same Rate`

(cherry picked from commit b1ba210332)

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-12-26 14:45:54 +05:30
ruthra kumar
ec337a1c9f Merge pull request #38945 from frappe/mergify/bp/version-14-hotfix/pr-38891
fix: incorrect price list in customer-wise item price report (backport #38891)
2023-12-26 12:16:52 +05:30
ruthra kumar
7577706a9d fix: incorrect price list in customer-wise item price report
(cherry picked from commit 9a00edb031)
2023-12-26 06:17:45 +00:00
mergify[bot]
81b5e6c5f1 fix: german translations for Lost Quotations (#38435)
* fix: german translations for Lost Quotations (#38435)

(cherry picked from commit 5952cfa673)

# Conflicts:
#	erpnext/translations/de.csv

* chore: resolve conflicts

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-12-24 16:24:31 +05:30
ruthra kumar
28f052d586 fix: first name error on customer creation (#38927)
fix: error on customer creation
2023-12-24 16:12:13 +05:30
s-aga-r
2eac618bba Merge pull request #38892 from s-aga-r/FIX-6565
fix: remove rows with zero consumed qty
2023-12-22 15:58:09 +05:30
Deepesh Garg
6f6de13813 Merge pull request #38815 from frappe/mergify/bp/version-14-hotfix/pr-38803
fix: groups for current accounts in German CoAs (#38803)
2023-12-22 08:40:28 +05:30
Deepesh Garg
be1a269f9f Merge pull request #38857 from frappe/revert-38422-mergify/bp/version-14-hotfix/pr-38250
Revert "fix: unset discount amount based on coupon code (backport #38250)"
2023-12-22 08:40:07 +05:30
Deepesh Garg
0d70be56d2 Merge pull request #38370 from pps190/backport/version-14-hotfix/38289
fix: display all item rate stop messages (#38289)
2023-12-22 08:38:19 +05:30
Deepesh Garg
50655821c1 Merge pull request #38770 from frappe/mergify/bp/version-14-hotfix/pr-38767
fix: skip JVs against bank accounts in tax report (#38767)
2023-12-22 08:37:41 +05:30
mergify[bot]
b254a72d41 perf: Drop unused/duplicate/sub-optimal indexes (backport #38884) (#38912)
* perf: Drop unused/duplicate/sub-optimal indexes (#38884)

* ci: enable more checks

* perf: Drop unused/duplicate indexes

(cherry picked from commit 787333896c)

# Conflicts:
#	erpnext/accounts/doctype/gl_entry/gl_entry.json
#	erpnext/patches.txt

* chore: conflicts

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-12-21 21:40:59 +05:30
rohitwaghchaure
2770ca1b65 fix: reset the incoming rate on changing of the warehouse (#38909) 2023-12-21 18:49:36 +05:30
mergify[bot]
0d7cb1a9be chore: additional_asset_cost field (backport #38904) (backport #38905) (#38908)
chore: additional_asset_cost field (backport #38904) (#38905)

* chore: additional_asset_cost field (#38904)

(cherry picked from commit 283763dfb2)

# Conflicts:
#	erpnext/assets/doctype/asset/asset.py

* chore: resolve conflicts in asset.py

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
(cherry picked from commit 3b9feffc00)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-12-21 17:12:01 +05:30
Frappe PR Bot
3abb91b2c4 chore(release): Bumped to Version 14.56.0
# [14.56.0](https://github.com/frappe/erpnext/compare/v14.55.2...v14.56.0) (2023-12-21)

### Features

* total_asset_cost field (backport [#38879](https://github.com/frappe/erpnext/issues/38879)) (backport [#38886](https://github.com/frappe/erpnext/issues/38886)) ([#38907](https://github.com/frappe/erpnext/issues/38907)) ([a63b8df](a63b8df867))
2023-12-21 11:41:51 +00:00
mergify[bot]
a63b8df867 feat: total_asset_cost field (backport #38879) (backport #38886) (#38907)
feat: total_asset_cost field (backport #38879) (#38886)

* feat: total_asset_cost field (#38879)

(cherry picked from commit d370c60a6c)

# Conflicts:
#	erpnext/assets/doctype/asset/asset.json
#	erpnext/assets/doctype/asset/asset.py
#	erpnext/patches.txt

* chore: resolve conflicts in asset.json

* chore: resolve conflicts in asset.py

* chore: resolve conflicts in patches.txt

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
(cherry picked from commit 8169c7de0d)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-12-21 17:10:04 +05:30
mergify[bot]
3b9feffc00 chore: additional_asset_cost field (backport #38904) (#38905)
* chore: additional_asset_cost field (#38904)

(cherry picked from commit 283763dfb2)

# Conflicts:
#	erpnext/assets/doctype/asset/asset.py

* chore: resolve conflicts in asset.py

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-12-21 16:56:32 +05:30
Frappe PR Bot
0c13cb829c chore(release): Bumped to Version 14.55.2
## [14.55.2](https://github.com/frappe/erpnext/compare/v14.55.1...v14.55.2) (2023-12-21)

### Bug Fixes

* reposting not fixing valuation rate for sales return using movin… (backport [#38895](https://github.com/frappe/erpnext/issues/38895)) ([#38900](https://github.com/frappe/erpnext/issues/38900)) ([8aba707](8aba707b6a))
2023-12-21 10:05:58 +00:00
mergify[bot]
8aba707b6a fix: reposting not fixing valuation rate for sales return using movin… (backport #38895) (#38900)
fix: reposting not fixing valuation rate for sales return using movin… (#38895)

fix: reposting not fixing valuation rate for sales return using moving average method
(cherry picked from commit 3a668bbe96)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-12-21 15:34:49 +05:30
rohitwaghchaure
98bfcc4c75 fix: do not reset the basic rate for the material receipt stock entry (#38896) 2023-12-21 14:13:55 +05:30
rohitwaghchaure
3a668bbe96 fix: reposting not fixing valuation rate for sales return using movin… (#38895)
fix: reposting not fixing valuation rate for sales return using moving average method
2023-12-21 14:13:34 +05:30
s-aga-r
f2f410093a fix: remove rows with zero consumed qty 2023-12-21 11:03:18 +05:30
mergify[bot]
8169c7de0d feat: total_asset_cost field (backport #38879) (#38886)
* feat: total_asset_cost field (#38879)

(cherry picked from commit d370c60a6c)

# Conflicts:
#	erpnext/assets/doctype/asset/asset.json
#	erpnext/assets/doctype/asset/asset.py
#	erpnext/patches.txt

* chore: resolve conflicts in asset.json

* chore: resolve conflicts in asset.py

* chore: resolve conflicts in patches.txt

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-12-20 22:57:37 +05:30
Frappe PR Bot
eefac19cc8 chore(release): Bumped to Version 14.55.1
## [14.55.1](https://github.com/frappe/erpnext/compare/v14.55.0...v14.55.1) (2023-12-20)

### Bug Fixes

* allow to set rate manually for service item in BOM (backport [#38880](https://github.com/frappe/erpnext/issues/38880)) ([#38881](https://github.com/frappe/erpnext/issues/38881)) ([afc0491](afc049154d))
2023-12-20 13:06:21 +00:00
mergify[bot]
afc049154d fix: allow to set rate manually for service item in BOM (backport #38880) (#38881)
fix: allow to set rate manually for service item in BOM (#38880)

(cherry picked from commit c2f692a4e4)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-12-20 18:34:31 +05:30
mergify[bot]
4d1ccd9e27 perf: use estimated rows instead of actual rows (backport #38830) (#38875)
* perf: use estimated rows instead of actual rows (#38830)

(cherry picked from commit 9983283f95)

# Conflicts:
#	erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py

* chore: conflicts

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-12-20 18:17:41 +05:30
Deepesh Garg
c48c8b8ddb Merge pull request #38858 from GursheenK/unset-discount-amt-coupon-code
fix: unset discount amount for removed coupons
2023-12-20 18:17:20 +05:30
rohitwaghchaure
c2f692a4e4 fix: allow to set rate manually for service item in BOM (#38880) 2023-12-20 17:53:48 +05:30
ruthra kumar
d94b5a318d Merge pull request #38872 from frappe/mergify/bp/version-14-hotfix/pr-38838
fix: use party account currency when party account is specified (backport #38838)
2023-12-20 13:09:17 +05:30
Devin Slauenwhite
30cb218638 fix(test): expect account currency when party account is specified.
(cherry picked from commit a09241e3c7)
2023-12-20 07:05:27 +00:00
Devin Slauenwhite
d9e0d55b88 fix: use party account currency when party account is specified
(cherry picked from commit c7b961ffa2)
2023-12-20 07:05:26 +00:00
ruthra kumar
1e9b4e2403 Merge pull request #38800 from pps190/backport/version-14-hotfix/37746
feat: in_party_currency option for AR/AP reports (backport #37746)
2023-12-20 12:25:35 +05:30
Frappe PR Bot
8657ba374a chore(release): Bumped to Version 14.55.0
# [14.55.0](https://github.com/frappe/erpnext/compare/v14.54.2...v14.55.0) (2023-12-20)

### Bug Fixes

* close PO on SCO close (backport [#38667](https://github.com/frappe/erpnext/issues/38667)) ([#38680](https://github.com/frappe/erpnext/issues/38680)) ([22b442d](22b442d593))
* **customer:** contact creation for companies ([#38055](https://github.com/frappe/erpnext/issues/38055)) ([ed9b38b](ed9b38bc8d))
* **customer:** quick form and integration fixes ([#37386](https://github.com/frappe/erpnext/issues/37386)) ([6ed7c66](6ed7c6610a))
* error while filtering on name on reconciliation tool ([30b6321](30b6321fd5))
* fetch exc rate of multi currency journals ([bf585de](bf585de558))
* fetch item_tax_template values if fields with fetch_from exisit ([24ae45c](24ae45ce84))
* get customers for leaderboard ([9dfe342](9dfe342838))
* get items for leaderboard ([812b301](812b301aab))
* get sales partner for leaderboard ([c63f873](c63f873a6f))
* get sales person for leaderboard ([df93447](df93447d30))
* get suppliers for leaderboard ([7477ff3](7477ff3b5c))
* if not budget then don't validate ([#38861](https://github.com/frappe/erpnext/issues/38861)) ([d375164](d375164100))
* incoming rate for sales return with Moving Average valuation method ([#38849](https://github.com/frappe/erpnext/issues/38849)) ([7fdac62](7fdac62393))
* incorrect limit ([#38818](https://github.com/frappe/erpnext/issues/38818)) ([e18dc5c](e18dc5cea3))
* Init internal child table values ([d593f81](d593f81a16))
* item variant with manufacturer ([#38845](https://github.com/frappe/erpnext/issues/38845)) ([e0c8ff1](e0c8ff10da))
* not able to cancel SCR with Batch ([#38817](https://github.com/frappe/erpnext/issues/38817)) ([fb5090f](fb5090fd3f))
* not able to make inter-company po from so ([#38826](https://github.com/frappe/erpnext/issues/38826)) ([23042df](23042dfc3c))
* on closed unreserved the production plan qty ([#38848](https://github.com/frappe/erpnext/issues/38848)) ([2184e8e](2184e8ef58))
* Reset SLA on issue doesn't work (backport [#38789](https://github.com/frappe/erpnext/issues/38789)) ([#38790](https://github.com/frappe/erpnext/issues/38790)) ([942f34a](942f34a734))
* supplier removed on selection of item (backport [#38712](https://github.com/frappe/erpnext/issues/38712)) ([#38732](https://github.com/frappe/erpnext/issues/38732)) ([dda95ea](dda95ea892))
* timezone aware SLA banner (backport [#38745](https://github.com/frappe/erpnext/issues/38745)) ([#38746](https://github.com/frappe/erpnext/issues/38746)) ([73d525e](73d525e84d))
* **ux:** don't override Item Name and Description in MR (backport [#38720](https://github.com/frappe/erpnext/issues/38720)) ([#38762](https://github.com/frappe/erpnext/issues/38762)) ([f7bfbd8](f7bfbd82dd))
* validation error on reconciling PE to Journals as Invoice ([9836205](9836205725))
* wrong currency in Stock Balance report (backport [#38778](https://github.com/frappe/erpnext/issues/38778)) ([#38779](https://github.com/frappe/erpnext/issues/38779)) ([e05b23c](e05b23c388))
* wrong paid and cn amount on pos invoice ([77da0da](77da0da945))

### Features

* **RFQ:** special properties in print preview (backport [#38725](https://github.com/frappe/erpnext/issues/38725)) ([#38726](https://github.com/frappe/erpnext/issues/38726)) ([2290750](229075048b))
* set lead name from email ([1469ca7](1469ca7d0b))

### Performance Improvements

* index `return_against` on delivery note (backport [#38827](https://github.com/frappe/erpnext/issues/38827)) ([#38831](https://github.com/frappe/erpnext/issues/38831)) ([4114760](4114760efd))
2023-12-20 04:46:08 +00:00
Deepesh Garg
3a8b7e9b96 Merge pull request #38852 from frappe/version-14-hotfix
chore: release v14
2023-12-20 10:14:59 +05:30
mergify[bot]
22b442d593 fix: close PO on SCO close (backport #38667) (#38680)
fix: close PO on SCO close

(cherry picked from commit b023e5d6b3)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-12-20 09:14:54 +05:30
mergify[bot]
f7bfbd82dd fix(ux): don't override Item Name and Description in MR (backport #38720) (#38762)
fix(ux): don't override Item Name and Description in MR

(cherry picked from commit 726ac6bda1)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-12-20 09:14:03 +05:30
rohitwaghchaure
d375164100 fix: if not budget then don't validate (#38861) 2023-12-19 18:19:20 +05:30
rohitwaghchaure
7fdac62393 fix: incoming rate for sales return with Moving Average valuation method (#38849) 2023-12-19 18:01:50 +05:30
rohitwaghchaure
2184e8ef58 fix: on closed unreserved the production plan qty (#38848) 2023-12-19 17:20:41 +05:30
Gursheen Anand
87e8dd0d63 fix: unset discount amount from coupon discounts 2023-12-19 15:56:59 +05:30
rohitwaghchaure
857843bc29 chore: fix test case (#38856) 2023-12-19 15:54:29 +05:30
Gursheen Kaur Anand
37a18299d0 Revert "fix: unset discount amount based on coupon code (backport #38250)" 2023-12-19 15:35:35 +05:30
rohitwaghchaure
e0c8ff10da fix: item variant with manufacturer (#38845) 2023-12-19 12:45:08 +05:30
ruthra kumar
61b02ec75c Merge pull request #38843 from frappe/mergify/bp/version-14-hotfix/pr-38797
fix: wrong paid and cn amount on pos invoice (backport #38797)
2023-12-19 11:33:25 +05:30
Dany Robert
939b7f7cbf test: partial payment for pos invoice
(cherry picked from commit 8772628912)
2023-12-19 05:33:10 +00:00
Dany Robert
77da0da945 fix: wrong paid and cn amount on pos invoice
(cherry picked from commit 5cb5e09dbb)
2023-12-19 05:33:09 +00:00
Devin Slauenwhite
226d0e0196 fix: display all item rate stop messages 2023-12-18 14:14:29 -05:00
mergify[bot]
4114760efd perf: index return_against on delivery note (backport #38827) (#38831)
* perf: index `return_against` on delivery note (#38827)

There's a multi-column index but that's useful IFF all parts of column
are part of query.

return against on it's own is VERY unique because it's a primary key, we
don't need a multi-column index here.

(cherry picked from commit 8d79365e0d)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/purchase_receipt.json

* chore: conflict

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-12-18 18:10:54 +05:30
rohitwaghchaure
23042dfc3c fix: not able to make inter-company po from so (#38826) 2023-12-18 17:44:17 +05:30
ruthra kumar
9216a852d4 Merge pull request #38824 from frappe/mergify/bp/version-14-hotfix/pr-38820
refactor: ignore ERR journals in Statment of Accounts (backport #38820)
2023-12-18 16:24:57 +05:30
ruthra kumar
6478c80359 refactor: ignore ERR journals in Statment of Accounts
(cherry picked from commit 39ef75e2d0)
2023-12-18 15:59:59 +05:30
rohitwaghchaure
e18dc5cea3 fix: incorrect limit (#38818) 2023-12-18 15:59:56 +05:30
rohitwaghchaure
fb5090fd3f fix: not able to cancel SCR with Batch (#38817) 2023-12-18 14:11:36 +05:30
barredterra
39aa36e44b fix: groups for current accounts in German CoAs
(cherry picked from commit 259f313af7)
2023-12-17 18:24:10 +00:00
Deepesh Garg
20a764a4a3 Merge pull request #38809 from vorasmit/fetch-item-taxes-v14
fix: fetch item_tax_template values if fields with fetch_from exisit (#38284)
2023-12-17 18:51:22 +05:30
Smit Vora
24ae45ce84 fix: fetch item_tax_template values if fields with fetch_from exisit 2023-12-17 14:03:37 +05:30
Deepesh Garg
a14e7f0e84 Merge pull request #38751 from frappe/mergify/bp/version-14-hotfix/pr-38505
feat: set lead name from email (#38505)
2023-12-16 22:29:43 +05:30
Devin Slauenwhite
7eee573fcb fixup! feat: in_party_currency option for AR/AP reports
fix: merge conflict typo
2023-12-16 16:29:01 +00:00
Richard Case
0894022695 chore: update tests 2023-12-16 16:15:00 +00:00
Richard Case
3c717db3fd feat: in_party_currency option for AR/AP reports 2023-12-16 16:14:44 +00:00
mergify[bot]
942f34a734 fix: Reset SLA on issue doesn't work (backport #38789) (#38790)
fix: Reset SLA on issue doesn't work (#38789)

This was broken since last refactor where it was spun off to work with
all types of doctypes but client side code was never adapted.

(cherry picked from commit fa1c7b663c)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-12-15 22:02:29 +05:30
Raffael Meyer
4821919695 Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-38505 2023-12-15 15:15:37 +01:00
Deepesh Garg
34549b62bd Merge pull request #38782 from frappe/mergify/bp/version-14-hotfix/pr-38691
fix: Init internal child table values (#38691)
2023-12-15 18:48:10 +05:30
Deepesh Garg
d593f81a16 fix: Init internal child table values
(cherry picked from commit 2588970d55)
2023-12-15 12:45:51 +00:00
mergify[bot]
e05b23c388 fix: wrong currency in Stock Balance report (backport #38778) (#38779)
fix: wrong currency in Stock Balance report

(cherry picked from commit 5a83a16e60)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-12-15 15:32:50 +05:30
ruthra kumar
d4c131b153 Merge pull request #38774 from frappe/mergify/bp/version-14-hotfix/pr-38766
fix: validation error on reconciling PE to Journals as Invoice (backport #38766)
2023-12-15 14:41:43 +05:30
ruthra kumar
9836205725 fix: validation error on reconciling PE to Journals as Invoice
With the same exchange rate on Journal Entry and Payment Entry,
reconcilition should not post exc gain/loss journal and should not
throw validation error

(cherry picked from commit 5eeb650dfd)
2023-12-15 13:20:38 +05:30
Gursheen Anand
98198d35ea fix: skip jvs against bank accounts
(cherry picked from commit f7b2380ec1)
2023-12-15 07:29:14 +00:00
Deepesh Garg
12cd7a7083 Merge pull request #38674 from frappe/mergify/bp/version-14-hotfix/pr-38672
fix: get data for leaderboard (#38672)
2023-12-15 10:15:41 +05:30
Deepesh Garg
e539152c6b Merge pull request #38753 from barredterra/contact-from-customer-creation
fix: address and contact from customer creation (backport #38055, #37386, #38060)
2023-12-15 10:14:58 +05:30
David Arnold
6ed7c6610a fix(customer): quick form and integration fixes (#37386) 2023-12-14 16:50:20 +01:00
barredterra
d6d717c315 test: parse full name 2023-12-14 16:47:46 +01:00
barredterra
935e23449f refactor: parse full name 2023-12-14 16:47:36 +01:00
David Arnold
ed9b38bc8d fix(customer): contact creation for companies (#38055) 2023-12-14 16:47:15 +01:00
Raffael Meyer
2c52f2ce13 chore: resolve conflicts 2023-12-14 13:51:47 +01:00
barredterra
1469ca7d0b feat: set lead name from email
(cherry picked from commit ceeb724acc)

# Conflicts:
#	erpnext/crm/doctype/lead/lead.json
2023-12-14 12:49:40 +00:00
mergify[bot]
73d525e84d fix: timezone aware SLA banner (backport #38745) (#38746)
fix: timezone aware SLA banner (#38745)

(cherry picked from commit eaf86a6461)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-12-14 15:59:19 +05:30
ruthra kumar
f6c611d026 Merge pull request #38744 from ruthra-kumar/filter_error_on_reconciliation_tool
fix: error while filtering on name on reconciliation tool
2023-12-14 15:10:12 +05:30
ruthra kumar
30b6321fd5 fix: error while filtering on name on reconciliation tool 2023-12-14 14:28:20 +05:30
Frappe PR Bot
20a26c3dd4 chore(release): Bumped to Version 14.54.2
## [14.54.2](https://github.com/frappe/erpnext/compare/v14.54.1...v14.54.2) (2023-12-14)

### Bug Fixes

* fetch exc rate of multi currency journals ([cdef0bd](cdef0bd503))
2023-12-14 06:20:30 +00:00
ruthra kumar
d975eccaab Merge pull request #38741 from frappe/mergify/bp/version-14/pr-38717
fix: fetch exc rate of multi currency journals on reconciliation (backport #38717)
2023-12-14 11:49:20 +05:30
ruthra kumar
cdef0bd503 fix: fetch exc rate of multi currency journals
(cherry picked from commit 1b3ba25220)
2023-12-14 06:00:23 +00:00
ruthra kumar
a714114cd9 Merge pull request #38736 from frappe/mergify/bp/version-14-hotfix/pr-38717
fix: fetch exc rate of multi currency journals on reconciliation (backport #38717)
2023-12-14 09:59:44 +05:30
ruthra kumar
bf585de558 fix: fetch exc rate of multi currency journals
(cherry picked from commit 1b3ba25220)
2023-12-14 04:06:43 +00:00
Frappe PR Bot
b7fd24074d chore(release): Bumped to Version 14.54.1
## [14.54.1](https://github.com/frappe/erpnext/compare/v14.54.0...v14.54.1) (2023-12-13)

### Bug Fixes

* supplier removed on selection of item (backport [#38712](https://github.com/frappe/erpnext/issues/38712)) (backport [#38732](https://github.com/frappe/erpnext/issues/38732)) ([#38735](https://github.com/frappe/erpnext/issues/38735)) ([d53a9f6](d53a9f6530))
2023-12-13 19:09:56 +00:00
mergify[bot]
d53a9f6530 fix: supplier removed on selection of item (backport #38712) (backport #38732) (#38735)
fix: supplier removed on selection of item (backport #38712) (#38732)

fix: supplier removed on selection of item (#38712)

(cherry picked from commit db24e24882)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit dda95ea892)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-12-14 00:38:47 +05:30
mergify[bot]
dda95ea892 fix: supplier removed on selection of item (backport #38712) (#38732)
fix: supplier removed on selection of item (#38712)

(cherry picked from commit db24e24882)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-12-13 19:05:52 +00:00
mergify[bot]
4908d03a88 Revert "fix(ux): don't update qty blindly" (backport #38728) (backport #38729) (#38733)
Revert "fix(ux): don't update qty blindly" (backport #38728) (#38729)

Revert "fix(ux): don't update qty blindly" (#38728)

(cherry picked from commit 6851c5042f)

Co-authored-by: Ankush Menat <ankush@frappe.io>
(cherry picked from commit 0d49dd3648)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-12-14 00:30:36 +05:30
mergify[bot]
229075048b feat(RFQ): special properties in print preview (backport #38725) (#38726)
feat: RFQ print preview

(cherry picked from commit 27f05145ae)

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
2023-12-14 00:10:58 +05:30
mergify[bot]
0d49dd3648 Revert "fix(ux): don't update qty blindly" (backport #38728) (#38729)
Revert "fix(ux): don't update qty blindly" (#38728)

(cherry picked from commit 6851c5042f)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-12-13 23:16:26 +05:30
Frappe PR Bot
214861d68f chore(release): Bumped to Version 14.54.0
# [14.54.0](https://github.com/frappe/erpnext/compare/v14.53.0...v14.54.0) (2023-12-12)

### Bug Fixes

* 1st row depr. sch. value of asset put to less than 180 days acc. to I.T. S. 32 ([#38697](https://github.com/frappe/erpnext/issues/38697)) ([753a1b5](753a1b511d))
* calc monthly_repayment_amount correctly in regenerate_repayment_schedule ([#38636](https://github.com/frappe/erpnext/issues/38636)) ([008400d](008400d287))
* format only if searched text contain link value text ([b28deaa](b28deaae2c))
* limit end date to current date ([cbe15e1](cbe15e159a))
* typo in unittest ([#38673](https://github.com/frappe/erpnext/issues/38673)) ([c87a3c7](c87a3c7793))
* **ux:** don't update qty blindly (backport [#38608](https://github.com/frappe/erpnext/issues/38608)) ([#38638](https://github.com/frappe/erpnext/issues/38638)) ([29cd474](29cd474f44))

### Features

* add employee number to client user bootinfo (backport [#38477](https://github.com/frappe/erpnext/issues/38477)) ([#38602](https://github.com/frappe/erpnext/issues/38602)) ([c2a137f](c2a137f0fb))

### Reverts

* Revert "refactor: bank transaction (backport #38182)" (#38653) ([7813efa](7813efad35)), closes [#38182](https://github.com/frappe/erpnext/issues/38182) [#38653](https://github.com/frappe/erpnext/issues/38653) [#38182](https://github.com/frappe/erpnext/issues/38182)
2023-12-12 16:12:27 +00:00
Deepesh Garg
e7b96209cd Merge pull request #38689 from frappe/version-14-hotfix
chore: release v14
2023-12-12 21:40:16 +05:30
Anand Baburajan
753a1b511d fix: 1st row depr. sch. value of asset put to less than 180 days acc. to I.T. S. 32 (#38697)
fix: 1st row value of asset put to less than 180 days acc. to IT S. 32
2023-12-12 20:53:44 +05:30
ruthra kumar
6715dd6167 Merge pull request #38677 from frappe/mergify/bp/version-14-hotfix/pr-38673
fix: typo in unittest (backport #38673)
2023-12-12 17:35:16 +05:30
ruthra kumar
a061798a6f Merge pull request #38686 from frappe/mergify/bp/version-14-hotfix/pr-38685
refactor: add `get_list` for virtual child doctypes (backport #38685)
2023-12-12 15:24:56 +05:30
ruthra kumar
fa79a7cdab refactor: add get_list for virtual child doctypes
(cherry picked from commit 15c90551b6)
2023-12-12 14:51:41 +05:30
ruthra kumar
6884aac2a0 Merge pull request #38683 from frappe/mergify/bp/version-14-hotfix/pr-38679
refactor(test): repost utility deletion flag test (backport #38679)
2023-12-12 13:33:17 +05:30
ruthra kumar
e61a12b4cb refactor(test): update repost settings before test case
(cherry picked from commit acb6e8e120)
2023-12-12 07:16:49 +00:00
ruthra kumar
4542a12787 refactor: increase limit and remove explicit call to start_repost
(cherry picked from commit ccff588563)
2023-12-12 07:16:49 +00:00
ruthra kumar
f0408c89ce refactor: remove explicit commit on repost
(cherry picked from commit a97b3db749)
2023-12-12 07:16:48 +00:00
ruthra kumar
7c080bd60c refactor(test): repost utility deletion flag test
(cherry picked from commit cc15f695b4)
2023-12-12 07:16:48 +00:00
Raffael Meyer
c87a3c7793 fix: typo in unittest (#38673)
(cherry picked from commit 6ad298adfc)
2023-12-12 04:26:10 +00:00
barredterra
038a30feb0 chore: deprecate unused method
(cherry picked from commit 956c3c50a0)
2023-12-12 04:24:39 +00:00
barredterra
c63f873a6f fix: get sales partner for leaderboard
(cherry picked from commit 40c1acc961)
2023-12-12 04:24:39 +00:00
barredterra
df93447d30 fix: get sales person for leaderboard
(cherry picked from commit 7babfd4ac4)
2023-12-12 04:24:39 +00:00
barredterra
7477ff3b5c fix: get suppliers for leaderboard
(cherry picked from commit 65df4b6aa8)
2023-12-12 04:24:38 +00:00
barredterra
812b301aab fix: get items for leaderboard
(cherry picked from commit 2721ee3a8d)
2023-12-12 04:24:38 +00:00
barredterra
9dfe342838 fix: get customers for leaderboard
(cherry picked from commit 137b5a6108)
2023-12-12 04:24:38 +00:00
Deepesh Garg
7813efad35 Revert "refactor: bank transaction (backport #38182)" (#38653)
Revert "refactor: bank transaction (#38182)"

This reverts commit a4b5fc8100.
2023-12-10 16:29:27 +05:30
mergify[bot]
29cd474f44 fix(ux): don't update qty blindly (backport #38608) (#38638)
fix(ux): don't update qty blindly

(cherry picked from commit 0156339f34)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-12-08 17:59:46 +05:30
Anand Baburajan
008400d287 fix: calc monthly_repayment_amount correctly in regenerate_repayment_schedule (#38636) 2023-12-08 16:34:29 +05:30
Shariq Ansari
4b9d9c0df4 Merge pull request #38626 from frappe/mergify/bp/version-14-hotfix/pr-38623
fix: format only if searched text contain link value text (backport #38623)
2023-12-07 20:51:16 +05:30
Shariq Ansari
b28deaae2c fix: format only if searched text contain link value text
(cherry picked from commit 08ed3cd313)
2023-12-07 15:17:48 +00:00
Ankush Menat
4e6b8ead8f chore: remove unused cache=True
(cherry picked from commit 6a47a2ceaf)
2023-12-06 20:55:35 +05:30
mergify[bot]
c2a137f0fb feat: add employee number to client user bootinfo (backport #38477) (#38602)
feat: add employee number to client user bootinfo (#38477)

(cherry picked from commit 525f656cc1)

Co-authored-by: Richard Case <110036763+casesolved-co-uk@users.noreply.github.com>
2023-12-06 20:55:06 +05:30
Deepesh Garg
f6fcb7ee26 Merge pull request #38571 from frappe/mergify/bp/version-14-hotfix/pr-38556
fix(minor): financial statements period end date (#38556)
2023-12-06 13:45:45 +05:30
Frappe PR Bot
973d06d96f chore(release): Bumped to Version 14.53.0
# [14.53.0](https://github.com/frappe/erpnext/compare/v14.52.1...v14.53.0) (2023-12-05)

### Bug Fixes

* `OperationalError` while selecting Serial No in `Warranty Claim` (backport [#38394](https://github.com/frappe/erpnext/issues/38394)) ([#38413](https://github.com/frappe/erpnext/issues/38413)) ([49296cd](49296cd5cb))
* better overlap logic for job card (backport [#38432](https://github.com/frappe/erpnext/issues/38432)) ([#38521](https://github.com/frappe/erpnext/issues/38521)) ([6db63c9](6db63c971e))
* consider the `Valuation Method` while picking incorrect SLE (backport [#38592](https://github.com/frappe/erpnext/issues/38592)) ([#38594](https://github.com/frappe/erpnext/issues/38594)) ([01b2a1f](01b2a1ffeb))
* debit credit mismatch in multi-currecy asset purchase receipt ([#38342](https://github.com/frappe/erpnext/issues/38342)) ([fd6a23c](fd6a23cf41))
* don't consider cancelled entries ([#38401](https://github.com/frappe/erpnext/issues/38401)) ([9345334](9345334196))
* don't show non-stock items in Stock Analytics report (backport [#38543](https://github.com/frappe/erpnext/issues/38543)) ([#38544](https://github.com/frappe/erpnext/issues/38544)) ([2e333e6](2e333e6802))
* don't update previous doc on rate change (backport [#38493](https://github.com/frappe/erpnext/issues/38493)) ([#38523](https://github.com/frappe/erpnext/issues/38523)) ([b88c7d6](b88c7d63de))
* exclude `invoice_doctypes` from party advance ([1042d4c](1042d4cfeb))
* exploded items in Subcontracting Receipt (backport [#38441](https://github.com/frappe/erpnext/issues/38441)) ([#38444](https://github.com/frappe/erpnext/issues/38444)) ([2908d96](2908d966b6))
* get dynamic link with parenttype contact ([12d3cda](12d3cda2d9))
* incorrect customer outstanding amount ([#38475](https://github.com/frappe/erpnext/issues/38475)) ([c07723d](c07723d49d))
* incorrect material request quantity in Production Plan (backport [#38566](https://github.com/frappe/erpnext/issues/38566)) ([#38578](https://github.com/frappe/erpnext/issues/38578)) ([70965ef](70965efd94))
* incorrect ordered qty for Subcontracting Order ([#38415](https://github.com/frappe/erpnext/issues/38415)) ([8e37087](8e370876cc))
* incorrect requested quantity for the subcontracting order (backport [#38455](https://github.com/frappe/erpnext/issues/38455)) ([#38471](https://github.com/frappe/erpnext/issues/38471)) ([497049b](497049b3fd))
* item group filter in sales person wise report ([eee9062](eee9062814))
* make create button translatable ([#38165](https://github.com/frappe/erpnext/issues/38165)) ([c71b31e](c71b31e3b8))
* no fstring in translation ([#38381](https://github.com/frappe/erpnext/issues/38381)) ([cec2574](cec25740ea))
* **pe:** show split alert only on splitting ([8d6d74c](8d6d74c237))
* **regional:** use net figures for sales calc ([#38260](https://github.com/frappe/erpnext/issues/38260)) ([d337533](d337533907))
* remove hardcoded, implicit rounding loss allowance ([419943a](419943a9d9))
* set cwip account before asset tests ([ecde1d5](ecde1d58b5))
* show item name as title instead of item group in BOM (backport [#38478](https://github.com/frappe/erpnext/issues/38478)) ([#38480](https://github.com/frappe/erpnext/issues/38480)) ([0df52d2](0df52d2c4f))
* SO ordered qty on PO item removal ([#38378](https://github.com/frappe/erpnext/issues/38378)) ([b7fe163](b7fe163de5))
* sql error while filtering on finance book in GL ([25ec1fc](25ec1fce61))
* unset discount amount based on coupon code ([7fa97fa](7fa97fa7b5))
* use `docstatus` instead of `status` (backport [#38439](https://github.com/frappe/erpnext/issues/38439)) ([#38442](https://github.com/frappe/erpnext/issues/38442)) ([2dc4b02](2dc4b02fc8))
* use predefined onload property `load_after_mapping` (backport [#38209](https://github.com/frappe/erpnext/issues/38209)) ([#38518](https://github.com/frappe/erpnext/issues/38518)) ([9f23b7e](9f23b7e46f))
* **ux:** make valuation field read only when it can't be modified (backport [#38450](https://github.com/frappe/erpnext/issues/38450)) ([#38463](https://github.com/frappe/erpnext/issues/38463)) ([bec7fb5](bec7fb5493))
* validate finance_books table length in asset ([#38584](https://github.com/frappe/erpnext/issues/38584)) ([14b908f](14b908f8ab))

### Features

* `Company` filter in `Stock Ledger Variance` report (backport [#38553](https://github.com/frappe/erpnext/issues/38553)) ([#38573](https://github.com/frappe/erpnext/issues/38573)) ([6c89f35](6c89f351f4))
* shift depreciation for assets [v14] [backport of [#38327](https://github.com/frappe/erpnext/issues/38327)] ([#38404](https://github.com/frappe/erpnext/issues/38404)) ([d8c3935](d8c3935e64))
2023-12-05 17:17:12 +00:00
Deepesh Garg
37bf57cf75 Merge pull request #38589 from frappe/version-14-hotfix
chore: release v14
2023-12-05 22:45:16 +05:30
Deepesh Garg
8d4c38f6b9 Merge branch 'version-14' into version-14-hotfix 2023-12-05 22:01:50 +05:30
mergify[bot]
01b2a1ffeb fix: consider the Valuation Method while picking incorrect SLE (backport #38592) (#38594)
* fix: incorrect SLE for `Moving Average` valuation method

(cherry picked from commit 8beec58670)

* feat: add `Valuation Method` column in `Stock Ledger Variance` report

(cherry picked from commit 16c297c2ec)

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-12-05 18:13:59 +05:30
ruthra kumar
b858d1ceeb Merge pull request #38585 from frappe/mergify/bp/version-14-hotfix/pr-38580
fix: sql error while filtering on finance book in GL (backport #38580)
2023-12-05 15:07:07 +05:30
Sherin KR
14b908f8ab fix: validate finance_books table length in asset (#38584) 2023-12-05 14:44:59 +05:30
ruthra kumar
25ec1fce61 fix: sql error while filtering on finance book in GL
(cherry picked from commit b1d9f3132d)
2023-12-05 09:11:39 +00:00
mergify[bot]
70965efd94 fix: incorrect material request quantity in Production Plan (backport #38566) (#38578)
fix: incorrect material request quantity in Production Plan (#38566)

(cherry picked from commit aaa9036eca)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-12-05 14:24:54 +05:30
Karol Walczysko
9f43e9e668 Fix: Asset JS fieldname depreciation_schedule to schedules in make_schedules_editable function. (#38583)
Fix fieldname depreciation_schedule to schedules

Renames the depreciation_schedule fieldname to schedules in the make_schedules_editable function.
2023-12-05 14:22:23 +05:30
ruthra kumar
47ef075759 Merge pull request #38576 from frappe/mergify/bp/version-14-hotfix/pr-38570
refactor: ignore unreconcile doc on PI cancel/delete (backport #38570)
2023-12-05 13:52:22 +05:30
ruthra kumar
4544727654 refactor: ingore on JE cancel as well
(cherry picked from commit 1a5d56977e)
2023-12-05 13:32:10 +05:30
mergify[bot]
6c89f351f4 feat: Company filter in Stock Ledger Variance report (backport #38553) (#38573)
feat: `Company` filter in `Stock Ledger Variance` report

(cherry picked from commit fb3421fcce)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-12-05 12:33:14 +05:30
ruthra kumar
35c06fabc5 refactor: ignore unreconcile doc on PI cancel/delete
(cherry picked from commit 4ca84eadb6)
2023-12-05 06:34:13 +00:00
Gursheen Anand
cbe15e159a fix: limit end date to current date
(cherry picked from commit ab6e92aae1)
2023-12-05 05:54:12 +00:00
Shariq Ansari
3411814ee3 Merge pull request #38563 from frappe/mergify/bp/version-14-hotfix/pr-38562
fix: get dynamic link with parenttype contact (backport #38562)
2023-12-04 19:22:28 +05:30
Shariq Ansari
12d3cda2d9 fix: get dynamic link with parenttype contact
(cherry picked from commit 3d7ad71b22)
2023-12-04 13:51:13 +00:00
ruthra kumar
efc6a8a044 Merge pull request #38546 from frappe/mergify/bp/version-14-hotfix/pr-38283
chore: move reconciliation cleanup patch to pre-model sync (backport #38283)
2023-12-04 14:33:44 +05:30
ruthra kumar
0d913a6401 Merge pull request #38549 from frappe/mergify/bp/version-14-hotfix/pr-38475
fix: incorrect customer outstanding amount (backport #38475)
2023-12-04 13:58:16 +05:30
ruthra kumar
2e3a860a46 chore: resolve conflict 2023-12-04 13:43:41 +05:30
NIYAZ RAZAK
c07723d49d fix: incorrect customer outstanding amount (#38475)
* fix: incorrect customer outstanding amount

* chore: resolve linting error

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

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
(cherry picked from commit 3df1d75bdd)
2023-12-04 08:10:42 +00:00
ruthra kumar
7551ba3851 chore: move reconciliation cleanup patch to pre-model
(cherry picked from commit f258ab5e98)

# Conflicts:
#	erpnext/patches.txt
2023-12-04 07:33:38 +00:00
ruthra kumar
67f149ee06 Merge pull request #38541 from frappe/mergify/bp/version-14-hotfix/pr-38148
refactor: convert Payment Reconciliation to virtual doctype (backport #38148)
2023-12-04 13:01:49 +05:30
ruthra kumar
d27e588672 chore: resolve conflicts 2023-12-04 12:21:19 +05:30
mergify[bot]
2e333e6802 fix: don't show non-stock items in Stock Analytics report (backport #38543) (#38544)
* fix(ux): stock-item filter for Item Code field

(cherry picked from commit ccdcb7dfcc)

* fix: don't show non-stock items in Stock Analytics report

(cherry picked from commit 01aadbef85)

* fix: `linter`

(cherry picked from commit 15fff84bb5)

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-12-04 12:18:31 +05:30
ruthra kumar
59773acdd2 chore: clear singles table and reconciliation related tables
(cherry picked from commit f31002636b)

# Conflicts:
#	erpnext/patches.txt
2023-12-04 06:31:05 +00:00
ruthra kumar
454d789232 chore: remove reconciliation defaults from patch
(cherry picked from commit b5dd0c8630)

# Conflicts:
#	erpnext/patches.txt
2023-12-04 06:31:04 +00:00
ruthra kumar
c349c89ae3 refactor: virtual doctype methods
(cherry picked from commit 9c7b19e0b7)
2023-12-04 06:31:04 +00:00
ruthra kumar
36dc7bd55e refactor: convert payment reconciliation tool to virtual doctype
(cherry picked from commit 3a51a3f37e)

# Conflicts:
#	erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json
2023-12-04 06:31:03 +00:00
ruthra kumar
5a8bf7bf7d Merge pull request #38539 from frappe/mergify/bp/version-14-hotfix/pr-38220
fix: exclude `invoice_doctypes` from party advance (backport #38220)
2023-12-04 11:19:24 +05:30
Dany Robert
1042d4cfeb fix: exclude invoice_doctypes from party advance
(cherry picked from commit f34ffc2062)
2023-12-04 05:31:28 +00:00
mergify[bot]
9f23b7e46f fix: use predefined onload property load_after_mapping (backport #38209) (#38518)
Co-authored-by: Sagar Vora <sagar@resilient.tech>
fix: use predefined onload property `load_after_mapping` (#38209)
2023-12-04 09:52:16 +05:30
mergify[bot]
b7fe163de5 fix: SO ordered qty on PO item removal (#38378)
* fix: SO ordered qty on PO item removal (#38378)

* fix: update ordered_qty for SO when PO items removed

* refactor: use cached value

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
(cherry picked from commit 9087e1443e)

# Conflicts:
#	erpnext/buying/doctype/purchase_order/purchase_order.py

* chore: resolve conflicts

---------

Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com>
2023-12-04 09:35:51 +05:30
ruthra kumar
8c9f4c8700 Merge pull request #38527 from frappe/mergify/bp/version-14-hotfix/pr-38525
fix: multiple minor fixes in report and Exchange Rate Revaluation (backport #38525)
2023-12-03 17:04:10 +05:30
ruthra kumar
419943a9d9 fix: remove hardcoded, implicit rounding loss allowance
(cherry picked from commit 64266c4d38)
2023-12-03 11:14:13 +00:00
ruthra kumar
eee9062814 fix: item group filter in sales person wise report
(cherry picked from commit f4d418ea6d)
2023-12-03 11:14:12 +00:00
mergify[bot]
6db63c971e fix: better overlap logic for job card (backport #38432) (#38521)
* fix: better overlap logic for job card (#38432)

(cherry picked from commit 74eab91042)

# Conflicts:
#	erpnext/manufacturing/doctype/job_card/job_card.py

* chore: fix conflicts

* chore: fixed existing_time_logs not defined

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-12-03 15:15:30 +05:30
mergify[bot]
b88c7d63de fix: don't update previous doc on rate change (backport #38493) (#38523)
fix: don't update previous doc on rate change

(cherry picked from commit 68f5dd3e7b)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-12-03 14:02:59 +05:30
mergify[bot]
9345334196 fix: don't consider cancelled entries (#38401)
* fix: don't consider cancelled entries

(cherry picked from commit adfcdb3b65)

* refactor: get outstanding journal entry using query builder

(cherry picked from commit ff27cccff4)

---------

Co-authored-by: Devin Slauenwhite <devin.slauenwhite@gmail.com>
2023-12-02 20:06:48 +05:30
Deepesh Garg
ae285f3417 Merge pull request #38422 from frappe/mergify/bp/version-14-hotfix/pr-38250
fix: unset discount amount based on coupon code (#38250)
2023-12-02 19:42:24 +05:30
mergify[bot]
b813964ee3 chore: changed sort_order to DESC for customer (backport #38498) (#38500)
chore: changed sort_order to DESC for customer (#38498)

(cherry picked from commit 6bc40373f2)

Co-authored-by: Sherin KR <sherinkrply@gmail.com>
2023-12-01 19:09:20 +05:30
ruthra kumar
3f311fcc1e Merge pull request #38485 from frappe/mergify/bp/version-14-hotfix/pr-38440
fix(pe): show split alert only on splitting (backport #38440)
2023-12-01 13:25:47 +05:30
Dany Robert
8d6d74c237 fix(pe): show split alert only on splitting
(cherry picked from commit 96b13c59c1)
2023-12-01 07:10:29 +00:00
mergify[bot]
0df52d2c4f fix: show item name as title instead of item group in BOM (backport #38478) (#38480)
fix: show item name as title instead of item group in BOM (#38478)

Item fields in BOM used to show Item Group when Items were set to show title as link fields. Now they show Item Name instead

(cherry picked from commit 3a66aefd2c)

Co-authored-by: Gughan Ravikumar <gughanrk@gmail.com>
2023-12-01 11:32:49 +05:30
mergify[bot]
497049b3fd fix: incorrect requested quantity for the subcontracting order (backport #38455) (#38471)
* fix: incorrect requested quantity for the subcontracting order

(cherry picked from commit 691e3bb24f)

# Conflicts:
#	erpnext/buying/doctype/purchase_order/purchase_order.py
#	erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py
#	erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json
#	erpnext/subcontracting/doctype/subcontracting_order_service_item/subcontracting_order_service_item.json

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2023-11-30 20:53:52 +05:30
mergify[bot]
bec7fb5493 fix(ux): make valuation field read only when it can't be modified (backport #38450) (#38463)
* fix(ux): make `basic_rate` field read-only based on purpose

(cherry picked from commit abc7d30024)

* fix(ux): make PR `rate` field read-only having PO ref

(cherry picked from commit ae294ee470)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json

* fix(ux): make PI `rate` field read-only having PR ref

(cherry picked from commit 3d4156cc7d)

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-11-30 17:32:58 +05:30
Frappe PR Bot
7b6f25cb9e chore(release): Bumped to Version 14.52.1
## [14.52.1](https://github.com/frappe/erpnext/compare/v14.52.0...v14.52.1) (2023-11-30)

### Bug Fixes

* use flt on outstanding on AR/AP summary report ([161b0da](161b0da29a))
2023-11-30 11:36:02 +00:00
ruthra kumar
865288976c Merge pull request #38459 from frappe/mergify/bp/version-14/pr-38457
fix: use flt on outstanding on AR/AP summary report (backport #38457)
2023-11-30 17:04:42 +05:30
ruthra kumar
00347cdc8e Merge pull request #38460 from frappe/mergify/bp/version-14-hotfix/pr-38457
refactor: use flt on outstanding on AR/AP summary report (backport #38457)
2023-11-30 16:52:15 +05:30
ruthra kumar
161b0da29a fix: use flt on outstanding on AR/AP summary report
(cherry picked from commit e4bdd3a28d)
2023-11-30 16:43:58 +05:30
ruthra kumar
b7556f9b30 refactor: use flt on outstanding on AR/AP summary report
(cherry picked from commit e4bdd3a28d)
2023-11-30 16:28:13 +05:30
Gursheen Kaur Anand
2b570ed68c Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-38250 2023-11-30 14:15:12 +05:30
ruthra kumar
d82a123cf8 Merge pull request #38447 from frappe/mergify/bp/version-14-hotfix/pr-38446
refactor: pass on filter to up front outstanding query as well (backport #38446)
2023-11-30 13:18:43 +05:30
ruthra kumar
e8ea6022f0 refactor: pass on filter to upfront outstanding query as well
(cherry picked from commit cfd3230c75)
2023-11-30 07:26:29 +00:00
mergify[bot]
2908d966b6 fix: exploded items in Subcontracting Receipt (backport #38441) (#38444)
* fix: exploded items in Subcontracting Receipt

(cherry picked from commit 62b4a263f8)

# Conflicts:
#	erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-11-30 12:15:52 +05:30
mergify[bot]
2dc4b02fc8 fix: use docstatus instead of status (backport #38439) (#38442)
fix: use `docstatus` instead of `status`

(cherry picked from commit 1423b38d50)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-11-30 12:11:53 +05:30
Deepesh Garg
f9f65a53fb Merge pull request #38430 from frappe/mergify/bp/version-14-hotfix/pr-38429
fix(minor): set capital WIP account before asset tests (#38429)
2023-11-30 08:14:58 +05:30
Gursheen Anand
ecde1d58b5 fix: set cwip account before asset tests
(cherry picked from commit ef8e4191cd)
2023-11-29 13:21:46 +00:00
Gursheen Anand
7fa97fa7b5 fix: unset discount amount based on coupon code
(cherry picked from commit 6518582ed3)

# Conflicts:
#	erpnext/public/js/utils/sales_common.js
2023-11-29 10:25:18 +00:00
rohitwaghchaure
72db68dd39 Merge pull request #38410 from frappe/mergify/bp/version-14-hotfix/pr-38165
fix: make create button translatable (backport #38165)
2023-11-29 13:04:34 +05:30
rohitwaghchaure
d0224cecac Merge pull request #38418 from frappe/mergify/bp/version-14-hotfix/pr-38415
fix: incorrect ordered qty for Subcontracting Order (backport #38415)
2023-11-29 13:03:04 +05:30
mergify[bot]
49296cd5cb fix: OperationalError while selecting Serial No in Warranty Claim (backport #38394) (#38413)
* refactor: use arrow function

(cherry picked from commit 1763824e5f)

* refactor: use DocType `Fetch From` instead of `frm.add_fetch`

(cherry picked from commit 01044ca8e9)

# Conflicts:
#	erpnext/support/doctype/warranty_claim/warranty_claim.json

* refactor: use `frm.set_query` to add filters

(cherry picked from commit 640dfab827)

* refactor: don't use `cur_frm`

(cherry picked from commit 9fadf5f426)

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-11-29 12:27:03 +05:30
s-aga-r
55e4951445 chore: conflicts 2023-11-29 12:25:16 +05:30
rohitwaghchaure
8e370876cc fix: incorrect ordered qty for Subcontracting Order (#38415)
(cherry picked from commit fddf341f44)

# Conflicts:
#	erpnext/controllers/tests/test_subcontracting_controller.py
2023-11-29 06:52:34 +00:00
Patrick Eissler
c71b31e3b8 fix: make create button translatable (#38165)
* fix: make all create buttons translatable

* style: use double quotes

---------

Co-authored-by: PatrickDenis-stack <77415730+PatrickDenis-stack@users.noreply.github.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
(cherry picked from commit 8e4b591ea2)
2023-11-29 04:53:26 +00:00
Deepesh Garg
44b11750d3 chore: v14 patch release (#38408)
* feat: shift depreciation for assets [v14] [backport of #38327] (#38404)

feat: shift depreciation for assets

* fix: no fstring in translation (#38381)

fix: no fstring in translation (#38381)

(cherry picked from commit 8f00481c5f)

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

* fix: debit credit mismatch in multi-currecy asset purchase receipt (#38342)

fix: debit credit mismatch in multi-currecy asset purchase receipt (#38342)

* fix: Debit credit mimatch in multicurrecy asset purchase receipt

* test: multi currency purchase receipt

* chore: update init files

* test: roolback

(cherry picked from commit add238c892)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>

* fix(regional): use net figures for sales calc (#38260)

fix(regional): use net figures for sales calc (#38260)

(cherry picked from commit 663bb8726c)

Co-authored-by: Dany Robert <danyrt@wahni.com>

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: Dany Robert <danyrt@wahni.com>
2023-11-29 10:20:53 +05:30
mergify[bot]
d337533907 fix(regional): use net figures for sales calc (#38260)
fix(regional): use net figures for sales calc (#38260)

(cherry picked from commit 663bb8726c)

Co-authored-by: Dany Robert <danyrt@wahni.com>
2023-11-29 09:09:08 +05:30
mergify[bot]
fd6a23cf41 fix: debit credit mismatch in multi-currecy asset purchase receipt (#38342)
fix: debit credit mismatch in multi-currecy asset purchase receipt (#38342)

* fix: Debit credit mimatch in multicurrecy asset purchase receipt

* test: multi currency purchase receipt

* chore: update init files

* test: roolback

(cherry picked from commit add238c892)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-11-29 09:07:42 +05:30
mergify[bot]
cec25740ea fix: no fstring in translation (#38381)
fix: no fstring in translation (#38381)

(cherry picked from commit 8f00481c5f)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-11-29 09:06:04 +05:30
Frappe PR Bot
9170ffc371 chore(release): Bumped to Version 14.52.0
# [14.52.0](https://github.com/frappe/erpnext/compare/v14.51.0...v14.52.0) (2023-11-29)

### Features

* shift depreciation for assets [v14] [backport of [#38327](https://github.com/frappe/erpnext/issues/38327)] ([#38404](https://github.com/frappe/erpnext/issues/38404)) ([53d083a](53d083a121))
2023-11-29 00:48:28 +00:00
Anand Baburajan
6c805fff15 Merge pull request #38405 from frappe/mergify/bp/version-14/pr-38404
feat: shift depreciation for assets [v14] [backport of #38327] (backport #38404)
2023-11-29 06:16:47 +05:30
Anand Baburajan
53d083a121 feat: shift depreciation for assets [v14] [backport of #38327] (#38404)
feat: shift depreciation for assets
(cherry picked from commit d8c3935e64)
2023-11-29 00:46:14 +00:00
Anand Baburajan
d8c3935e64 feat: shift depreciation for assets [v14] [backport of #38327] (#38404)
feat: shift depreciation for assets
2023-11-29 06:15:22 +05:30
Frappe PR Bot
47d37aa628 chore(release): Bumped to Version 14.51.0
# [14.51.0](https://github.com/frappe/erpnext/compare/v14.50.0...v14.51.0) (2023-11-28)

### Bug Fixes

* Alert message and make sure invoice due dates are different for effective test ([0813f02](0813f02ca5))
* allow on submit for child table fields ([d72fbe9](d72fbe9125))
* annual income and expenses in digest ([7dce68b](7dce68bdcc))
* check reposting settings before allowing editable si ([f777d24](f777d24679))
* condition in other bundle utils ([e96ef73](e96ef7338e))
* create contact if existing customer doesn't have contact ([bb2aa90](bb2aa904b2))
* do not set repost flag without validating voucher ([3ad6d08](3ad6d0890a))
* filter bundle items based on disabled check ([8a3a068](8a3a0688ba))
* fiscal year using future date ([b0aa4ef](b0aa4efd3d))
* has_product_bundle util to only check for enabled bundles ([fd19ac5](fd19ac55e3))
* job card overlap validation (backport [#38345](https://github.com/frappe/erpnext/issues/38345)) ([#38347](https://github.com/frappe/erpnext/issues/38347)) ([9cf8955](9cf8955d24))
* Merge conflicts ([e7183e3](e7183e3ea9))
* Negative Qty and Rates in SO/PO ([#38253](https://github.com/frappe/erpnext/issues/38253)) ([50f3b01](50f3b01eb7))
* Payment Reco Issue and chart of account importer ([730a3bb](730a3bb7ce))
* Re-add no.of rows split in alert message ([fb19e7f](fb19e7f15e))
* **regional:** item wise tax calc issue ([0448d56](0448d56a4f))
* Server Error while creating Product Bundle (backport [#38377](https://github.com/frappe/erpnext/issues/38377)) ([#38379](https://github.com/frappe/erpnext/issues/38379)) ([ca6a788](ca6a78882c))
* skip disabled bundles for non-report utils ([445e9c9](445e9c9041))
* skip fixed assets in parent ([5dcd806](5dcd806176))
* stock availability not showing ([#38382](https://github.com/frappe/erpnext/issues/38382)) ([3929cc3](3929cc30ca))
* validation for existing bundles ([ae960e7](ae960e735a))

### Features

* add disabled field in product bundle ([8d5efc9](8d5efc901d))
* new Report "Lost Quotations" ([#38309](https://github.com/frappe/erpnext/issues/38309)) ([1773e31](1773e31499))
2023-11-28 16:24:55 +00:00
rohitwaghchaure
6260241c55 Merge pull request #38384 from frappe/version-14-hotfix
chore: release v14
2023-11-28 21:53:33 +05:30
Shariq Ansari
6871331ff3 Merge pull request #38399 from frappe/mergify/bp/version-14-hotfix/pr-38398
fix(CRM): create contact if existing customer doesn't have contact (backport #38398)
2023-11-28 20:45:08 +05:30
Shariq Ansari
bb2aa904b2 fix: create contact if existing customer doesn't have contact
(cherry picked from commit 23b0b8ba36)
2023-11-28 14:19:59 +00:00
mergify[bot]
e490bd7785 refactor(Translations): prisoner exchange with HRMS (backport #37376) (#37403)
* refactor: migrate translations to HRMS

* chore: resolve conflicts

* style: remove whitespace

To make the linter happy

---------

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
2023-11-28 17:50:28 +05:30
mergify[bot]
ca6a78882c fix: Server Error while creating Product Bundle (backport #38377) (#38379)
* fix: product bundle search input

(cherry picked from commit 729fc738af)

* fix: don't select all fields

(cherry picked from commit 8c3713b649)

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-11-28 15:26:36 +05:30
Raffael Meyer
321ac4c5f0 style: remove whitespace (#38388)
To make the linter happy
2023-11-28 15:22:35 +05:30
rohitwaghchaure
3929cc30ca fix: stock availability not showing (#38382)
fix: stock availability now showing
2023-11-28 15:16:35 +05:30
mergify[bot]
25f67923ea chore: fix flaky test case (backport #38369) (#38372)
chore: fix flaky test case (#38369)

(cherry picked from commit ad3634be7c)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-11-28 08:36:46 +05:30
ruthra kumar
5dc7faba0c Merge pull request #38358 from frappe/mergify/bp/version-14-hotfix/pr-38354
refactor: handle rounding loss on AR/AP reports (backport #38354)
2023-11-27 12:16:27 +05:30
ruthra kumar
2aa9171027 refactor: handle rounding loss on AR/AP reports
(cherry picked from commit 592ce45da7)
2023-11-27 06:22:44 +00:00
mergify[bot]
730a3bb7ce fix: Payment Reco Issue and chart of account importer
fix: Payment Reco Issue and chart of account importer
2023-11-27 10:30:46 +05:30
ruthra kumar
e9a2b002d8 Merge pull request #38355 from frappe/mergify/bp/version-14-hotfix/pr-38081
fix: check reposting ledger settings before setting repost flag (backport #38081)
2023-11-27 09:28:59 +05:30
ruthra kumar
faf025454f chore: resolve conflict 2023-11-27 09:08:23 +05:30
Marica
50f3b01eb7 fix: Negative Qty and Rates in SO/PO (#38253)
* fix: Don't allow negative qty in SO/PO

* fix: Type casting for safe comparisons

* fix: Grammar in error message

* fix: Negative rates should be allowed via Update Items in SO/PO

* fix: Use `non_negative` property in docfield & emove code validation
2023-11-27 09:06:27 +05:30
Gursheen Anand
f777d24679 fix: check reposting settings before allowing editable si
(cherry picked from commit 894ae1fe0f)
2023-11-27 03:35:38 +00:00
Gursheen Anand
d72fbe9125 fix: allow on submit for child table fields
(cherry picked from commit 5fae2f6d57)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
2023-11-27 03:35:37 +00:00
Gursheen Anand
3ad6d0890a fix: do not set repost flag without validating voucher
(cherry picked from commit ad5edbb1de)
2023-11-27 03:35:37 +00:00
Gursheen Anand
776c884403 refactor: validate reposting settings for editables inv
(cherry picked from commit 780b827adc)
2023-11-27 03:35:37 +00:00
mergify[bot]
a4b5fc8100 refactor: bank transaction (#38182)
refactor: bank transaction (#38182)

(cherry picked from commit 5426b93387)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-11-26 18:58:04 +05:30
Deepesh Garg
b0547b5619 Merge pull request #38074 from frappe/mergify/bp/version-14-hotfix/pr-37859
fix: `split_invoices_based_on_payment_terms` (#37859)
2023-11-26 18:15:11 +05:30
mergify[bot]
9cf8955d24 fix: job card overlap validation (backport #38345) (#38347)
* fix: job card overlap validation (#38345)

(cherry picked from commit d8245cef72)

# Conflicts:
#	erpnext/manufacturing/doctype/job_card/job_card.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-11-26 17:44:02 +05:30
Deepesh Garg
f7c820ffad Merge pull request #38331 from frappe/mergify/bp/version-14-hotfix/pr-38309
feat: new Report "Lost Quotations" (#38309)
2023-11-25 17:36:27 +05:30
Raffael Meyer
1773e31499 feat: new Report "Lost Quotations" (#38309)
(cherry picked from commit 477d9fa87e)
2023-11-24 21:27:50 +00:00
Deepesh Garg
466d0849e8 Merge pull request #38313 from frappe/mergify/bp/version-14-hotfix/pr-36797
fix(regional): item wise tax calc issue (#36797)
2023-11-24 16:31:03 +05:30
Deepesh Garg
28b6afcb10 Merge pull request #38315 from frappe/mergify/bp/version-14-hotfix/pr-38269
fix: skip fixed assets in product bundle (#38269)
2023-11-24 16:30:05 +05:30
Deepesh Garg
bdde30a456 Merge pull request #38324 from frappe/mergify/bp/version-14-hotfix/pr-38273
fix: annual income and expenses in email digest (#38273)
2023-11-24 16:29:16 +05:30
Gursheen Kaur Anand
aea169d4d4 chore: resolve conflicts 2023-11-24 15:30:19 +05:30
Gursheen Anand
b0aa4efd3d fix: fiscal year using future date
(cherry picked from commit 728cc9f725)
2023-11-24 09:51:29 +00:00
Gursheen Anand
7dce68bdcc fix: annual income and expenses in digest
(cherry picked from commit 52305e3000)

# Conflicts:
#	erpnext/accounts/utils.py
2023-11-24 09:51:29 +00:00
Frappe PR Bot
436ba6f244 chore(release): Bumped to Version 14.50.0
# [14.50.0](https://github.com/frappe/erpnext/compare/v14.49.0...v14.50.0) (2023-11-24)

### Bug Fixes

* don't depreciate non-depreciable assets on scrapping [v14] ([#38294](https://github.com/frappe/erpnext/issues/38294)) ([5261aba](5261aba81f))
* partial cancel of gle and ple (backport [#35609](https://github.com/frappe/erpnext/issues/35609)) ([80bc235](80bc235d6e))
* patch - Duplicate entry quality inspection parameter (backport [#38262](https://github.com/frappe/erpnext/issues/38262)) ([#38263](https://github.com/frappe/erpnext/issues/38263)) ([756f08b](756f08b9ba))
* Supplier `Primary Contact` (backport [#38268](https://github.com/frappe/erpnext/issues/38268)) ([#38285](https://github.com/frappe/erpnext/issues/38285)) ([f170cb9](f170cb9214))

### Features

* add Bank Transaction to connections in Journal and Payment Entry ([#38297](https://github.com/frappe/erpnext/issues/38297)) ([06c6da5](06c6da5c0d))

### Performance Improvements

* optimize total_purchase_cost update ([a88d322](a88d322e4f))
2023-11-24 08:47:31 +00:00
ruthra kumar
79ded4bf19 Merge pull request #38320 from ruthra-kumar/version-14
chore: release v14
2023-11-24 14:16:16 +05:30
ruthra kumar
5af23e760c Merge branch 'version-14-hotfix' into version-14 2023-11-24 13:26:25 +05:30
ruthra kumar
7601861f21 Merge pull request #38318 from frappe/mergify/bp/version-14-hotfix/pr-38298
perf: optimize update_purchase_cost method  (backport #38298)
2023-11-24 12:20:57 +05:30
ruthra kumar
1d1af26f7a chore: resolve conflict 2023-11-24 11:54:55 +05:30
ruthra kumar
47093bebd3 refactor: update project costing based on frequency
(cherry picked from commit dd016e6ced)
2023-11-24 06:20:04 +00:00
ruthra kumar
205d971253 refactor: make update_project_cost optional through Buying Settings
(cherry picked from commit 0fe6dcd742)
2023-11-24 06:20:04 +00:00
ruthra kumar
49a787b1a8 refactor: provide UI button to recalculate when needed
(cherry picked from commit bcbe6c4a53)

# Conflicts:
#	erpnext/projects/doctype/project/project.py
2023-11-24 06:20:04 +00:00
ruthra kumar
a88d322e4f perf: optimize total_purchase_cost update
(cherry picked from commit aa17110bde)
2023-11-24 06:20:02 +00:00
Deepesh Garg
dfdaad0111 Merge pull request #38300 from frappe/mergify/bp/version-14-hotfix/pr-38297
feat: add Bank Transaction to connections in Journal and Payment Entry (#38297)
2023-11-24 10:06:59 +05:30
ruthra kumar
d80e8c6b15 Merge pull request #38311 from frappe/mergify/bp/version-14-hotfix/pr-38296
chore: index to speed up basic submit/cancel functions on purchase invoice (backport #38296)
2023-11-24 10:04:59 +05:30
Gursheen Anand
d7d5317b0e chore: linting issues
(cherry picked from commit 1657337887)
2023-11-24 04:30:38 +00:00
Gursheen Anand
445e9c9041 fix: skip disabled bundles for non-report utils
(cherry picked from commit 362f377f61)
2023-11-24 04:30:38 +00:00
Gursheen Anand
e96ef7338e fix: condition in other bundle utils
(cherry picked from commit 8bdb61cb87)
2023-11-24 04:30:38 +00:00
Gursheen Anand
ae960e735a fix: validation for existing bundles
(cherry picked from commit 67f43d37df)
2023-11-24 04:30:38 +00:00
Gursheen Anand
fd19ac55e3 fix: has_product_bundle util to only check for enabled bundles
(cherry picked from commit 3543f86c63)
2023-11-24 04:30:38 +00:00
Gursheen Anand
8a3a0688ba fix: filter bundle items based on disabled check
(cherry picked from commit 874774fe6c)
2023-11-24 04:30:37 +00:00
Gursheen Anand
8d5efc901d feat: add disabled field in product bundle
(cherry picked from commit ee76af7681)
2023-11-24 04:30:37 +00:00
Gursheen Anand
5dcd806176 fix: skip fixed assets in parent
(cherry picked from commit f9713eeb56)
2023-11-24 04:30:36 +00:00
Dany Robert
0448d56a4f fix(regional): item wise tax calc issue
(cherry picked from commit 89ddf3272e)
2023-11-24 04:28:23 +00:00
ruthra kumar
f74132b0be chore: speed up Purchase Invoice cancellation
(cherry picked from commit 1efff268b0)
2023-11-24 04:16:22 +00:00
ruthra kumar
6353deacf0 chore: index to speed up queries on JE child table reference
(cherry picked from commit 24fcd67f8b)
2023-11-24 04:16:21 +00:00
mergify[bot]
756f08b9ba fix: patch - Duplicate entry quality inspection parameter (backport #38262) (#38263)
fix: patch - Duplicate entry quality inspection parameter (#38262)

(cherry picked from commit 0ca7527f7a)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-11-23 22:39:14 +05:30
Raffael Meyer
2293696ab4 chore: resolve conflicts 2023-11-23 15:10:29 +01:00
Raffael Meyer
06c6da5c0d feat: add Bank Transaction to connections in Journal and Payment Entry (#38297)
* feat(Payment Entry): Bank Transaction connection

* feat(Journal Entry): Bank Transaction connection

(cherry picked from commit 3be345e605)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/payment_entry.json
2023-11-23 13:03:38 +00:00
Anand Baburajan
5261aba81f fix: don't depreciate non-depreciable assets on scrapping [v14] (#38294)
fix: don't depreciate non-depreciable assets on scrapping
2023-11-23 14:15:40 +05:30
mergify[bot]
f170cb9214 fix: Supplier Primary Contact (backport #38268) (#38285)
fix: Supplier `Primary Contact`

(cherry picked from commit 627165dc7c)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-11-23 11:45:57 +05:30
ruthra kumar
8055773425 Merge pull request #38281 from frappe/mergify/bp/version-14-hotfix/pr-38258
refactor: rename 'Unreconcile Payments' doctype to singular 'Unreconcile Payment' (backport #38258)
2023-11-23 11:09:11 +05:30
ruthra kumar
82fa58d4e3 chore: update new unreconcile doctype name in JS and PY files
(cherry picked from commit 74f9e34182)
2023-11-23 05:09:23 +00:00
ruthra kumar
57c6d616e6 chore: rename 'unreconcile payments' to 'unreconcile payment'
(cherry picked from commit 9006c9b747)
2023-11-23 05:09:23 +00:00
ruthra kumar
639512a7c2 Merge pull request #38279 from frappe/mergify/bp/version-14-hotfix/pr-38257
refactor: optmize outstanding amount query (backport #38257)
2023-11-23 10:16:11 +05:30
ruthra kumar
ac13e7362c refactor: optimize outstanding vouchers query
(cherry picked from commit 8b04c1d4f6)
2023-11-23 04:12:42 +00:00
Marica
1e7dfd3071 Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-37859 2023-11-22 20:51:30 +05:30
marination
e7183e3ea9 fix: Merge conflicts 2023-11-22 16:16:37 +01:00
ruthra kumar
c4e57040b7 Merge pull request #38205 from vorasmit/partial-cancel
fix: partial cancel of gle and ple (backport #35609)
2023-11-22 10:27:05 +05:30
Smit Vora
c1a7d614a2 test: partial cancel for gl entries reversal 2023-11-22 10:07:38 +05:30
Frappe PR Bot
7392f9c662 chore(release): Bumped to Version 14.49.0
# [14.49.0](https://github.com/frappe/erpnext/compare/v14.48.1...v14.49.0) (2023-11-22)

### Bug Fixes

* add revaluation journal filter in Payable report ([d0698b3](d0698b32bb))
* attributes were mandatory for manufacturers ([430980a](430980a836))
* duplicate field in `Closing Stock Balance` ([#38105](https://github.com/frappe/erpnext/issues/38105)) ([1f16c47](1f16c47a2c))
* incorrect incoming rate for serial and batch items in standalone debit note ([#38121](https://github.com/frappe/erpnext/issues/38121)) ([9a34518](9a34518e54))
* pass check permission in render_address ([1ccd5e4](1ccd5e4ff5))
* payment entry rounding error ([49735bc](49735bc120))
* remove ESS role when not mapped to employee (backport [#37867](https://github.com/frappe/erpnext/issues/37867)) ([#38132](https://github.com/frappe/erpnext/issues/38132)) ([bc01007](bc01007c16))
* round `unreconciled_amount` before asserting ([392ee2e](392ee2e0fd))
* set asset's valuation_rate according to asset quantity (backport [#38254](https://github.com/frappe/erpnext/issues/38254)) ([#38255](https://github.com/frappe/erpnext/issues/38255)) ([00def82](00def82843))
* set default asset quantity as 1 [v14] ([#38224](https://github.com/frappe/erpnext/issues/38224)) ([3daf6f8](3daf6f822a))
* show party values when naming by is not naming series ([dd76695](dd76695d3a))
* Supplier Quotation fields ([#37963](https://github.com/frappe/erpnext/issues/37963)) ([883eaee](883eaee014))
* test case for rounded total with cash disc ([9b5185a](9b5185adc9))
* **Timesheet:** reset billing hours equal to hours if they exceed actual hours (backport [#38134](https://github.com/frappe/erpnext/issues/38134)) ([#38152](https://github.com/frappe/erpnext/issues/38152)) ([c7c751e](c7c751ecd0))
* **Timesheet:** warn user if billing hours > actual hours instead of resetting  (backport [#38239](https://github.com/frappe/erpnext/issues/38239)) ([#38240](https://github.com/frappe/erpnext/issues/38240)) ([e08f114](e08f1145c9))
* update modified timestamp ([2849e0d](2849e0daed))
* valuation rate in report Item Prices ([#38161](https://github.com/frappe/erpnext/issues/38161)) ([a70696e](a70696ea77))
* wrong round off and rounded total ([296433a](296433a1dd))

### Features

* add `Supplier Delivery Note` field in SCR (backport [#38127](https://github.com/frappe/erpnext/issues/38127)) ([#38155](https://github.com/frappe/erpnext/issues/38155)) ([8d4a19c](8d4a19cecf))
* Add accounting dimensions to Supplier Quotation ([51e33e1](51e33e1556))
* virtual parent doctype ([8dbf2ce](8dbf2ced79))
2023-11-22 04:16:31 +00:00
Deepesh Garg
aa6ef76043 Merge pull request #38247 from frappe/version-14-hotfix
chore: release v14
2023-11-22 09:43:45 +05:30
Deepesh Garg
f6f5ff0be9 Merge branch 'version-14' into version-14-hotfix 2023-11-22 00:28:38 +05:30
mergify[bot]
00def82843 fix: set asset's valuation_rate according to asset quantity (backport #38254) (#38255)
fix: set asset's valuation_rate according to asset quantity (#38254)

(cherry picked from commit e2bb4e2baa)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-11-21 22:53:31 +05:30
mergify[bot]
e08f1145c9 fix(Timesheet): warn user if billing hours > actual hours instead of resetting (backport #38239) (#38240)
fix(Timesheet): warn user if billing hours > actual hours instead of resetting  (#38239)

* revert: "fix(Timesheet): reset billing hours equal to hours if they exceed actual hours"

This reverts commit 0ec8034507.

* fix: warn user if billing hours > actual hours

(cherry picked from commit ac91030b31)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2023-11-21 13:41:08 +05:30
ruthra kumar
ddf82cf433 Merge pull request #38235 from frappe/mergify/bp/version-14-hotfix/pr-38234
test: prevent rounding loss based validation error (backport #38234)
2023-11-21 12:30:34 +05:30
ruthra kumar
59a5e132fe test: prevent rounding loss based validation error
(cherry picked from commit 56e991b7f4)
2023-11-21 06:41:36 +00:00
ruthra kumar
7fc7105c48 Merge pull request #38217 from rtdany10/payment-reco-rounding-issue
fix: round `unreconciled_amount` before asserting
2023-11-21 11:33:42 +05:30
Anand Baburajan
3daf6f822a fix: set default asset quantity as 1 [v14] (#38224)
fix: set default asset quantity as 1
2023-11-20 22:27:25 +05:30
ruthra kumar
d7278477d6 Merge pull request #38213 from frappe/mergify/bp/version-14-hotfix/pr-38210
refactor: extend billed amount update flag to POS Invoice as well (backport #38210)
2023-11-20 15:02:44 +05:30
ruthra kumar
d0571530eb Merge pull request #38215 from frappe/mergify/bp/version-14-hotfix/pr-38212
refactor: update scheduled job for bulk transaction (backport #38212)
2023-11-20 14:49:10 +05:30
Dany Robert
392ee2e0fd fix: round unreconciled_amount before asserting 2023-11-20 14:36:58 +05:30
ruthra kumar
562a55122b refactor: update scheduled job for bulk transaction
(cherry picked from commit fb06ad7330)
2023-11-20 08:51:08 +00:00
ruthra kumar
b226adeb2f Merge pull request #38150 from frappe/mergify/bp/version-14-hotfix/pr-38038
refactor: supercharge Bulk actions (backport #38038)
2023-11-20 14:16:27 +05:30
ruthra kumar
12f027b3f4 chore: resolve conflict 2023-11-20 14:15:33 +05:30
ruthra kumar
9f8aa7c59d refactor: add flag to POS Invoice
(cherry picked from commit 83a13e22b7)

# Conflicts:
#	erpnext/accounts/doctype/pos_invoice/pos_invoice.json
2023-11-20 08:36:52 +00:00
ruthra kumar
40f904391e refactor: set default for 'update_billed_amount_in_delivery_note'
(cherry picked from commit ee0c64215d)
2023-11-20 08:36:51 +00:00
ruthra kumar
bfb85527dc Merge pull request #38196 from frappe/mergify/bp/version-14-hotfix/pr-38159
refactor: provision to truncate `remarks` in General Ledger and Accounts Receivable/Payable reports (backport #38159)
2023-11-20 13:30:39 +05:30
ruthra kumar
b868e2bd5b chore: resolve conflicts 2023-11-20 12:33:51 +05:30
Sagar Vora
b7e74b479f Merge pull request #38198 from frappe/mergify/bp/version-14-hotfix/pr-37963
fix: Supplier Quotation fields (backport #37963)
2023-11-20 11:46:25 +05:30
Sagar Vora
7c92ecdc9d chore: resolve conflicts 2023-11-20 11:39:15 +05:30
Smit Vora
80bc235d6e fix: partial cancel of gle and ple (backport #35609) 2023-11-20 10:56:56 +05:30
Vishakh Desai
883eaee014 fix: Supplier Quotation fields (#37963)
(cherry picked from commit c2bda2c705)

# Conflicts:
#	erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
2023-11-20 04:42:39 +00:00
ruthra kumar
ae319760a0 refactor: add substring logic in ar/ap report
(cherry picked from commit a9bf906545)
2023-11-20 04:38:04 +00:00
ruthra kumar
4e2d0aa892 refactor: provision to set remarks length in accounts settings
(cherry picked from commit 97090ff367)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
2023-11-20 04:38:04 +00:00
Deepesh Garg
5afb8b59c9 Merge pull request #38190 from frappe/mergify/bp/version-14-hotfix/pr-38171
fix: wrong round off and rounded total (#38171)
2023-11-19 19:44:04 +05:30
Deepesh Garg
8136657dfc Merge pull request #38146 from GursheenK/show-party-values-when-naming-series-unset
fix(minor): show party values when naming by is not naming series
2023-11-19 19:23:57 +05:30
Dany Robert
9b5185adc9 fix: test case for rounded total with cash disc
(cherry picked from commit cc60c328fe)
2023-11-19 13:49:06 +00:00
Dany Robert
296433a1dd fix: wrong round off and rounded total
(cherry picked from commit 3a487bd33a)
2023-11-19 13:49:05 +00:00
Deepesh Garg
5033f189f9 Merge pull request #38172 from frappe/mergify/bp/version-14-hotfix/pr-38142
feat: Add accounting dimensions to Supplier Quotation (#38142)
2023-11-19 19:10:39 +05:30
Deepesh Garg
d85185db2e Merge pull request #38175 from frappe/mergify/bp/version-14-hotfix/pr-38163
fix: attributes were mandatory for manufacturers (#38163)
2023-11-19 19:08:46 +05:30
Deepesh Garg
76b6e0407d Merge pull request #38179 from frappe/mergify/bp/version-14-hotfix/pr-38161
fix: valuation rate in report Item Prices (#38161)
2023-11-19 18:47:40 +05:30
Deepesh Garg
2fdc575441 Merge pull request #38181 from pps190/fix-pe-precision-bp
fix: payment entry rounding error (#38177)
2023-11-19 18:47:01 +05:30
Deepesh Garg
7b9afa43ff chore: resolve conflicts 2023-11-19 18:34:47 +05:30
Devin Slauenwhite
49735bc120 fix: payment entry rounding error 2023-11-18 15:09:44 +00:00
Patrick Eissler
a70696ea77 fix: valuation rate in report Item Prices (#38161)
Co-authored-by: PatrickDenis-stack <77415730+PatrickDenis-stack@users.noreply.github.com>
(cherry picked from commit 32f622ef80)
2023-11-18 14:55:12 +00:00
barredterra
2849e0daed fix: update modified timestamp
Was missing in 434c2a1815
2023-11-18 15:44:36 +01:00
Deepesh Garg
da27c0fb2d chore: resolve conflicts 2023-11-18 20:10:28 +05:30
PatrickDenis-stack
430980a836 fix: attributes were mandatory for manufacturers
(cherry picked from commit 434c2a1815)
2023-11-18 14:38:57 +00:00
Deepesh Garg
51e33e1556 feat: Add accounting dimensions to Supplier Quotation
(cherry picked from commit 089da459f7)

# Conflicts:
#	erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
#	erpnext/patches.txt
2023-11-18 14:11:16 +00:00
mergify[bot]
8d4a19cecf feat: add Supplier Delivery Note field in SCR (backport #38127) (#38155)
* feat: add `Supplier Delivery Note` field in SCR

(cherry picked from commit da80e4dbce)

# Conflicts:
#	erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-11-18 18:53:55 +05:30
s-aga-r
9a34518e54 fix: incorrect incoming rate for serial and batch items in standalone debit note (#38121) 2023-11-18 18:48:25 +05:30
Sagar Vora
39841f9edd Merge pull request #38168 from frappe/mergify/bp/version-14-hotfix/pr-38167
fix: pass check permission in `render_address` (backport #38167)
2023-11-18 17:39:38 +05:30
Vishakh Desai
1ccd5e4ff5 fix: pass check permission in render_address
(cherry picked from commit 45299fe4b3)
2023-11-18 12:08:24 +00:00
Gursheen Kaur Anand
05d514ca8e Merge branch 'version-14-hotfix' into show-party-values-when-naming-series-unset 2023-11-17 20:36:11 +05:30
mergify[bot]
c7c751ecd0 fix(Timesheet): reset billing hours equal to hours if they exceed actual hours (backport #38134) (#38152)
fix(Timesheet): reset billing hours equal to hours if they exceed actual hours (#38134)

(cherry picked from commit 20c6e9fca2)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2023-11-17 18:28:10 +05:30
ruthra kumar
fa1f623801 refactor: raise exception on invalid date
(cherry picked from commit a393a6b76c)
2023-11-17 10:56:24 +00:00
ruthra kumar
8dbe753a25 refactor: update traceback on retry
(cherry picked from commit a52a1b49af)
2023-11-17 10:56:24 +00:00
ruthra kumar
a060000b70 refactor: support list view filters
(cherry picked from commit 93295bf25b)
2023-11-17 10:56:24 +00:00
ruthra kumar
b615cfc540 chore: resolve linting issue
(cherry picked from commit 73639db910)
2023-11-17 10:56:24 +00:00
ruthra kumar
32b6eed691 refactor: rollback for retries and UI alerts
(cherry picked from commit c320288690)
2023-11-17 10:56:23 +00:00
ruthra kumar
ad03008a1a refactor: barebones retry functionality
(cherry picked from commit 0aa1636d04)
2023-11-17 10:56:23 +00:00
ruthra kumar
2ec1a2f862 chore: show retried in list view
(cherry picked from commit 194d70f8a0)
2023-11-17 10:56:23 +00:00
ruthra kumar
f5f48f7480 refactor: add basic functionalities
(cherry picked from commit af35590549)
2023-11-17 10:56:23 +00:00
ruthra kumar
8dbf2ced79 feat: virtual parent doctype
(cherry picked from commit a248b13cc3)
2023-11-17 10:56:22 +00:00
ruthra kumar
b951dbfced chore: make from_doctype readonly
(cherry picked from commit b0dfc936a1)
2023-11-17 10:56:22 +00:00
ruthra kumar
863a200989 chore: add indexes
(cherry picked from commit 73090fa130)
2023-11-17 10:56:22 +00:00
ruthra kumar
8dafe103bd chore: add list view filters
(cherry picked from commit ade09bc709)
2023-11-17 10:56:22 +00:00
ruthra kumar
eda9800c93 refactor: simplify logging logic bulk_transaction
(cherry picked from commit ebd74a4e5b)
2023-11-17 10:56:22 +00:00
ruthra kumar
6d983b5f45 chore: rearrange fields
(cherry picked from commit c4f8f3613f)
2023-11-17 10:56:21 +00:00
ruthra kumar
65614497ed chore: convert child to normal table
(cherry picked from commit e5a8ad54e2)
2023-11-17 10:56:21 +00:00
ruthra kumar
785bec98d3 chore: remove 'Bulk Transaction Log' doctype
(cherry picked from commit 815c616f18)
2023-11-17 10:56:21 +00:00
Gursheen Kaur Anand
6b08aa2198 Merge branch 'version-14-hotfix' into show-party-values-when-naming-series-unset 2023-11-17 14:21:52 +05:30
Gursheen Anand
dd76695d3a fix: show party values when naming by is not naming series 2023-11-17 12:50:08 +05:30
ruthra kumar
85853295c4 Merge pull request #38138 from frappe/mergify/bp/version-14-hotfix/pr-38119
fix: add revaluation journal filter in Payable report (backport #38119)
2023-11-17 10:04:51 +05:30
ruthra kumar
d0698b32bb fix: add revaluation journal filter in Payable report
(cherry picked from commit 134201794a)
2023-11-17 10:02:13 +05:30
mergify[bot]
bc01007c16 fix: remove ESS role when not mapped to employee (backport #37867) (#38132)
fix: remove ESS role when not mapped to employee (#37867)

* fix: remove ESS role when not mapped to employee

* fix: emp role removal on unlinking

* fix: test case for user employee role mapping

* fix: mapped employee and user on creation

(cherry picked from commit 56b8d1b927)

Co-authored-by: Dany Robert <danyrt@wahni.com>
2023-11-16 20:27:53 +05:30
mergify[bot]
58cb4303ee chore: change read only condition of asset quantity field (backport #38111) (#38112)
chore: change read only condition of asset quantity field (#38111)

chore: change read only condition of asset quantity
(cherry picked from commit 6e0362dee8)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-11-15 18:01:28 +05:30
Deepesh Garg
56c70d300b Merge pull request #38106 from frappe/mergify/bp/version-14-hotfix/pr-38105
fix: duplicate field (#38105)
2023-11-15 13:29:35 +05:30
Arjun
1f16c47a2c fix: duplicate field in Closing Stock Balance (#38105)
(cherry picked from commit 908b21f7fd)
2023-11-15 07:06:34 +00:00
mergify[bot]
d1a8202624 chore: refetch item images on transaction save (backport #38095) (#38098)
* chore: refetch item images on transaction save (#38095)

chore: re fetch item images on transaction save
(cherry picked from commit e93a19ffb5)

# Conflicts:
#	erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
#	erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
#	erpnext/selling/doctype/quotation_item/quotation_item.json

* chore: fix conflict in pos_invoice_item.json

* chore: fix conflict in sales_invoice_item.json

* chore: fix conflict in quotation_item.json

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-11-14 20:11:23 +05:30
marination
fb19e7f15e fix: Re-add no.of rows split in alert message
(cherry picked from commit 1fc5844025)
2023-11-13 10:46:39 +00:00
marination
c840f30a20 style: Remove spaces introduced via merge conflict
(cherry picked from commit 4b4b176fcf)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/test_payment_entry.py
2023-11-13 10:46:39 +00:00
marination
0813f02ca5 fix: Alert message and make sure invoice due dates are different for effective test
- Make invoice due dates are different so that the invoice with the earliest due date is allocated first in the test
- Translate voucher type, simplify alert message. The invoice could be "split" into 1 row, no. of rows in the message seems unnecessary.

(cherry picked from commit 56ac3424d2)
2023-11-13 10:46:38 +00:00
marination
a7e2867526 test: get_outstanding_reference_documents (triggered via UI)
(cherry picked from commit 162c0497d1)
2023-11-13 10:46:38 +00:00
marination
077f3928cf refactor: split_invoices_based_on_payment_terms
- Invoices were in the wrong order due to the logic. The invoices with payment terms were added first and the rest after.
- Overly long function with unnecessary loops (reduced to one main loop) and complexity
- The split row as per payment terms was not ordered. So the second installment was allocated first

(cherry picked from commit 6bd56d2d5f)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/payment_entry.py
2023-11-13 10:46:38 +00:00
352 changed files with 8430 additions and 54552 deletions

View File

@@ -17,7 +17,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 18
node-version: 20
- name: Setup dependencies
run: |

View File

@@ -5,7 +5,7 @@ fail_fast: false
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
rev: v4.3.0
hooks:
- id: trailing-whitespace
files: "erpnext.*"
@@ -15,6 +15,10 @@ repos:
args: ['--branch', 'develop']
- id: check-merge-conflict
- id: check-ast
- id: check-json
- id: check-toml
- id: check-yaml
- id: debug-statements
- repo: https://github.com/PyCQA/flake8
rev: 5.0.4

View File

@@ -3,7 +3,7 @@ import inspect
import frappe
__version__ = "14.48.1"
__version__ = "14.61.3"
def get_default_company(user=None):

View File

@@ -53,8 +53,13 @@
},
"II. Forderungen und sonstige Vermögensgegenstände": {
"is_group": 1,
"Ford. a. Lieferungen und Leistungen": {
"Forderungen aus Lieferungen und Leistungen mit Kontokorrent": {
"account_number": "1400",
"account_type": "Receivable",
"is_group": 1
},
"Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": {
"account_number": "1410",
"account_type": "Receivable"
},
"Durchlaufende Posten": {
@@ -180,8 +185,13 @@
},
"IV. Verbindlichkeiten aus Lieferungen und Leistungen": {
"is_group": 1,
"Verbindlichkeiten aus Lieferungen u. Leistungen": {
"Verbindlichkeiten aus Lieferungen und Leistungen mit Kontokorrent": {
"account_number": "1600",
"account_type": "Payable",
"is_group": 1
},
"Verbindlichkeiten aus Lieferungen und Leistungen ohne Kontokorrent": {
"account_number": "1610",
"account_type": "Payable"
}
},

View File

@@ -407,13 +407,10 @@
"Bewertungskorrektur zu Forderungen aus Lieferungen und Leistungen": {
"account_number": "9960"
},
"Debitoren": {
"is_group": 1,
"account_number": "10000"
},
"Forderungen aus Lieferungen und Leistungen": {
"Forderungen aus Lieferungen und Leistungen mit Kontokorrent": {
"account_number": "1200",
"account_type": "Receivable"
"account_type": "Receivable",
"is_group": 1
},
"Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": {
"account_number": "1210"
@@ -1138,18 +1135,15 @@
"Bewertungskorrektur zu Verb. aus Lieferungen und Leistungen": {
"account_number": "9964"
},
"Kreditoren": {
"account_number": "70000",
"Verb. aus Lieferungen und Leistungen mit Kontokorrent": {
"account_number": "3300",
"account_type": "Payable",
"is_group": 1,
"Wareneingangs-­Verrechnungskonto" : {
"Wareneingangs-Verrechnungskonto" : {
"account_number": "70001",
"account_type": "Stock Received But Not Billed"
}
},
"Verb. aus Lieferungen und Leistungen": {
"account_number": "3300",
"account_type": "Payable"
},
"Verb. aus Lieferungen und Leistungen ohne Kontokorrent": {
"account_number": "3310"
},

View File

@@ -68,7 +68,12 @@
"enable_party_matching",
"enable_fuzzy_matching",
"tab_break_dpet",
"show_balance_in_coa"
"show_balance_in_coa",
"reports_tab",
"remarks_section",
"general_ledger_remarks_length",
"column_break_lvjk",
"receivable_payable_remarks_length"
],
"fields": [
{
@@ -429,6 +434,34 @@
"fieldname": "show_balance_in_coa",
"fieldtype": "Check",
"label": "Show Balances in Chart Of Accounts"
},
{
"fieldname": "reports_tab",
"fieldtype": "Tab Break",
"label": "Reports"
},
{
"default": "0",
"description": "Truncates 'Remarks' column to set character length",
"fieldname": "general_ledger_remarks_length",
"fieldtype": "Int",
"label": "General Ledger"
},
{
"default": "0",
"description": "Truncates 'Remarks' column to set character length",
"fieldname": "receivable_payable_remarks_length",
"fieldtype": "Int",
"label": "Accounts Receivable/Payable"
},
{
"fieldname": "column_break_lvjk",
"fieldtype": "Column Break"
},
{
"fieldname": "remarks_section",
"fieldtype": "Section Break",
"label": "Remarks Column Length"
}
],
"icon": "icon-cog",
@@ -436,7 +469,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-07-27 15:05:34.000264",
"modified": "2023-11-20 09:37:47.650347",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -11,6 +11,7 @@
{
"fieldname": "company",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Company",
"options": "Company",
@@ -19,7 +20,7 @@
],
"istable": 1,
"links": [],
"modified": "2020-05-01 12:32:34.044911",
"modified": "2024-01-03 11:13:02.669632",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Allowed To Transact With",
@@ -28,5 +29,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -137,7 +137,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance",
args: {
bank_account: frm.doc.bank_account,
till_date: frm.doc.bank_statement_from_date,
till_date: frappe.datetime.add_days(frm.doc.bank_statement_from_date, -1)
},
callback: (response) => {
frm.set_value("account_opening_balance", response.message);

View File

@@ -2,6 +2,7 @@
# For license information, please see license.txt
import frappe
from frappe.model.docstatus import DocStatus
from frappe.utils import flt
from erpnext.controllers.status_updater import StatusUpdater
@@ -68,7 +69,7 @@ class BankTransaction(StatusUpdater):
"payment_entry": voucher["payment_name"],
"allocated_amount": 0.0, # Temporary
}
child = self.append("payment_entries", pe)
self.append("payment_entries", pe)
added = True
# runs on_update_after_submit
@@ -393,3 +394,21 @@ 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
def remove_from_bank_transaction(doctype, docname):
"""Remove a (cancelled) voucher from all Bank Transactions."""
for bt_name in get_reconciled_bank_transactions(doctype, docname):
bt = frappe.get_doc("Bank Transaction", bt_name)
if bt.docstatus == DocStatus.cancelled():
continue
modified = False
for pe in bt.payment_entries:
if pe.payment_document == doctype and pe.payment_entry == docname:
bt.remove(pe)
modified = True
if modified:
bt.save()

View File

@@ -2,10 +2,10 @@
# See license.txt
import json
import unittest
import frappe
from frappe import utils
from frappe.model.docstatus import DocStatus
from frappe.tests.utils import FrappeTestCase
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
@@ -81,6 +81,29 @@ class TestBankTransaction(FrappeTestCase):
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
self.assertFalse(clearance_date)
def test_cancel_voucher(self):
bank_transaction = frappe.get_doc(
"Bank Transaction",
dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"),
)
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1700))
vouchers = json.dumps(
[
{
"payment_doctype": "Payment Entry",
"payment_name": payment.name,
"amount": bank_transaction.unallocated_amount,
}
]
)
reconcile_vouchers(bank_transaction.name, vouchers)
payment.reload()
payment.cancel()
bank_transaction.reload()
self.assertEqual(bank_transaction.docstatus, DocStatus.submitted())
self.assertEqual(bank_transaction.unallocated_amount, 1700)
self.assertEqual(bank_transaction.payment_entries, [])
# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
def test_debit_credit_output(self):
bank_transaction = frappe.get_doc(

View File

@@ -1,457 +1,152 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"actions": [],
"autoname": "naming_series:",
"beta": 0,
"creation": "2018-06-18 16:51:49.994750",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"naming_series",
"user",
"date",
"from_time",
"time",
"expense",
"custody",
"returns",
"outstanding_amount",
"payments",
"net_amount",
"amended_from"
],
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "POS-CLO-",
"fieldname": "naming_series",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Series",
"length": 0,
"no_copy": 0,
"options": "POS-CLO-",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "user",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "User",
"length": 0,
"no_copy": 0,
"options": "User",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Today",
"fieldname": "date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "from_time",
"fieldtype": "Time",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "From Time",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "time",
"fieldtype": "Time",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "To Time",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"reqd": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0.00",
"fieldname": "expense",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Expense",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"label": "Expense"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0.00",
"fieldname": "custody",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Custody",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"label": "Custody"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0.00",
"fieldname": "returns",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Returns",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "2",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
"precision": "2"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0.00",
"fieldname": "outstanding_amount",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Outstanding Amount",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0.0",
"fieldname": "payments",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Payments",
"length": 0,
"no_copy": 0,
"options": "Cashier Closing Payments",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"options": "Cashier Closing Payments"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "net_amount",
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Net Amount",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"read_only": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Cashier Closing",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
"read_only": 1
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-02-19 08:35:24.157327",
"links": [],
"modified": "2023-12-28 13:15:46.858427",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Cashier Closing",
"name_case": "",
"naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}
"states": [],
"track_changes": 1
}

View File

@@ -113,7 +113,7 @@ def generate_data_from_csv(file_doc, as_dict=False):
if as_dict:
data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
else:
if not row[1]:
if not row[1] and len(row) > 1:
row[1] = row[0]
row[3] = row[2]
data.append(row)

View File

@@ -192,7 +192,7 @@ class ExchangeRateRevaluation(Document):
# round off balance based on currency precision
# and consider debit-credit difference allowance
currency_precision = get_currency_precision()
rounding_loss_allowance = float(rounding_loss_allowance) or 0.05
rounding_loss_allowance = float(rounding_loss_allowance)
for acc in account_details:
acc.balance_in_account_currency = flt(acc.balance_in_account_currency, currency_precision)
if abs(acc.balance_in_account_currency) <= rounding_loss_allowance:

View File

@@ -41,7 +41,7 @@ def test_record_generator():
]
start = 2012
end = now_datetime().year + 5
end = now_datetime().year + 25
for year in range(start, end):
test_records.append(
{

View File

@@ -138,8 +138,7 @@
"label": "Against Voucher Type",
"oldfieldname": "against_voucher_type",
"oldfieldtype": "Data",
"options": "DocType",
"search_index": 1
"options": "DocType"
},
{
"fieldname": "against_voucher",
@@ -158,8 +157,7 @@
"label": "Voucher Type",
"oldfieldname": "voucher_type",
"oldfieldtype": "Select",
"options": "DocType",
"search_index": 1
"options": "DocType"
},
{
"fieldname": "voucher_no",
@@ -291,4 +289,4 @@
"search_fields": "voucher_no,account,posting_date,against_voucher",
"sort_field": "modified",
"sort_order": "DESC"
}
}

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', 'Journal Entry', 'Repost Payment Ledger', 'Asset', 'Asset Movement', 'Repost Accounting Ledger'];
frm.ignore_doctypes_on_cancel_all = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Repost Payment Ledger", "Asset", "Asset Movement", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"];
},
refresh: function(frm) {
@@ -51,7 +51,7 @@ frappe.ui.form.on("Journal Entry", {
}, __('Make'));
}
erpnext.accounts.unreconcile_payments.add_unreconcile_btn(frm);
erpnext.accounts.unreconcile_payment.add_unreconcile_btn(frm);
},
before_save: function(frm) {
if ((frm.doc.docstatus == 0) && (!frm.doc.is_system_generated)) {

View File

@@ -548,8 +548,16 @@
"icon": "fa fa-file-text",
"idx": 176,
"is_submittable": 1,
"links": [],
"modified": "2023-08-10 14:32:22.366895",
"links": [
{
"is_child_table": 1,
"link_doctype": "Bank Transaction Payments",
"link_fieldname": "payment_entry",
"parent_doctype": "Bank Transaction",
"table_fieldname": "payment_entries"
}
],
"modified": "2023-11-23 12:11:04.128015",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",

View File

@@ -78,6 +78,20 @@ class JournalEntry(AccountsController):
if not self.title:
self.title = self.get_title()
def submit(self):
if len(self.accounts) > 100:
msgprint(_("The task has been enqueued as a background job."), alert=True)
self.queue_action("submit", timeout=4600)
else:
return self._submit()
def cancel(self):
if len(self.accounts) > 100:
msgprint(_("The task has been enqueued as a background job."), alert=True)
self.queue_action("cancel", timeout=4600)
else:
return self._cancel()
def on_submit(self):
self.validate_cheque_info()
self.check_credit_limit()
@@ -98,6 +112,8 @@ class JournalEntry(AccountsController):
"Repost Payment Ledger Items",
"Repost Accounting Ledger",
"Repost Accounting Ledger Items",
"Unreconcile Payment",
"Unreconcile Payment Entries",
)
self.make_gl_entries(1)
self.update_advance_paid()
@@ -496,7 +512,7 @@ class JournalEntry(AccountsController):
).format(d.reference_name, d.account)
)
else:
dr_or_cr = "debit" if d.credit > 0 else "credit"
dr_or_cr = "debit" if flt(d.credit) > 0 else "credit"
valid = False
for jvd in against_entries:
if flt(jvd[dr_or_cr]) > 0:

View File

@@ -81,7 +81,6 @@
},
{
"account": "Sales - _TC",
"cost_center": "_Test Cost Center - _TC",
"credit_in_account_currency": 400.0,
"debit_in_account_currency": 0.0,
"doctype": "Journal Entry Account",

View File

@@ -203,7 +203,8 @@
"fieldtype": "Select",
"label": "Reference Type",
"no_copy": 1,
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement\nPayment Entry"
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement\nPayment Entry",
"search_index": 1
},
{
"fieldname": "reference_name",
@@ -211,7 +212,8 @@
"in_list_view": 1,
"label": "Reference Name",
"no_copy": 1,
"options": "reference_type"
"options": "reference_type",
"search_index": 1
},
{
"depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])",
@@ -278,13 +280,14 @@
"fieldtype": "Data",
"hidden": 1,
"label": "Reference Detail No",
"no_copy": 1
"no_copy": 1,
"search_index": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2023-06-16 14:11:13.507807",
"modified": "2023-11-23 11:44:25.841187",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",

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', 'Journal Entry', 'Repost Payment Ledger','Repost Accounting Ledger', 'Unreconcile Payments', 'Unreconcile Payment Entries'];
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', 'Repost Payment Ledger', 'Repost Accounting Ledger', 'Unreconcile Payment', 'Unreconcile Payment Entries', 'Bank Transaction'];
if(frm.doc.__islocal) {
if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null);
@@ -158,7 +158,7 @@ frappe.ui.form.on('Payment Entry', {
}, __('Actions'));
}
erpnext.accounts.unreconcile_payments.add_unreconcile_btn(frm);
erpnext.accounts.unreconcile_payment.add_unreconcile_btn(frm);
},
validate_company: (frm) => {
@@ -631,7 +631,7 @@ frappe.ui.form.on('Payment Entry', {
get_outstanding_invoices_or_orders: function(frm, get_outstanding_invoices, get_orders_to_be_billed) {
const today = frappe.datetime.get_today();
const fields = [
let fields = [
{fieldtype:"Section Break", label: __("Posting Date")},
{fieldtype:"Date", label: __("From Date"),
fieldname:"from_posting_date", default:frappe.datetime.add_days(today, -30)},
@@ -646,18 +646,29 @@ frappe.ui.form.on('Payment Entry', {
fieldname:"outstanding_amt_greater_than", default: 0},
{fieldtype:"Column Break"},
{fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"},
{fieldtype:"Section Break"},
{fieldtype:"Link", label:__("Cost Center"), fieldname:"cost_center", options:"Cost Center",
"get_query": function() {
return {
"filters": {"company": frm.doc.company}
}
];
if (frm.dimension_filters) {
let column_break_insertion_point = Math.ceil((frm.dimension_filters.length)/2);
fields.push({fieldtype:"Section Break"});
frm.dimension_filters.map((elem, idx)=>{
fields.push({
fieldtype: "Link",
label: elem.document_type == "Cost Center" ? "Cost Center" : elem.label,
options: elem.document_type,
fieldname: elem.fieldname || elem.document_type
});
if(idx+1 == column_break_insertion_point) {
fields.push({fieldtype:"Column Break"});
}
},
{fieldtype:"Column Break"},
});
}
fields = fields.concat([
{fieldtype:"Section Break"},
{fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1},
];
]);
let btn_text = "";
@@ -829,7 +840,6 @@ frappe.ui.form.on('Payment Entry', {
else
total_negative_outstanding += Math.abs(flt(row.outstanding_amount));
})
var allocated_negative_outstanding = 0;
if (
(frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
@@ -844,6 +854,7 @@ frappe.ui.form.on('Payment Entry', {
var allocated_positive_outstanding = paid_amount + allocated_negative_outstanding;
} else if (in_list(["Customer", "Supplier"], frm.doc.party_type)) {
total_negative_outstanding = flt(total_negative_outstanding, precision("outstanding_amount"))
if(paid_amount > total_negative_outstanding) {
if(total_negative_outstanding == 0) {
frappe.msgprint(
@@ -919,7 +930,7 @@ frappe.ui.form.on('Payment Entry', {
if(frm.doc.payment_type == "Receive"
&& frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions
&& frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) {
unallocated_amount = (frm.doc.base_received_amount + total_deductions + flt(frm.doc.base_total_taxes_and_charges)
unallocated_amount = (frm.doc.base_received_amount + total_deductions - flt(frm.doc.base_total_taxes_and_charges)
- frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
} else if (frm.doc.payment_type == "Pay"
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions

View File

@@ -739,8 +739,16 @@
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-06-19 11:38:04.387219",
"links": [
{
"is_child_table": 1,
"link_doctype": "Bank Transaction Payments",
"link_fieldname": "payment_entry",
"parent_doctype": "Bank Transaction",
"table_fieldname": "payment_entries"
}
],
"modified": "2023-11-23 12:07:20.887885",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",
@@ -786,4 +794,4 @@
"states": [],
"title_field": "title",
"track_changes": 1
}
}

View File

@@ -9,8 +9,11 @@ import frappe
from frappe import ValidationError, _, qb, scrub, throw
from frappe.utils import cint, comma_or, flt, getdate, nowdate
from frappe.utils.data import comma_and, fmt_money
from pypika import Case
from pypika.functions import Coalesce, Sum
import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions
from erpnext.accounts.doctype.bank_account.bank_account import (
get_bank_account_details,
get_party_bank_account,
@@ -107,7 +110,7 @@ class PaymentEntry(AccountsController):
"Repost Payment Ledger Items",
"Repost Accounting Ledger",
"Repost Accounting Ledger Items",
"Unreconcile Payments",
"Unreconcile Payment",
"Unreconcile Payment Entries",
)
super(PaymentEntry, self).on_cancel()
@@ -913,8 +916,11 @@ class PaymentEntry(AccountsController):
):
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 = flt(
sum(
abs(flt(d.outstanding_amount)) for d in self.get("references") if flt(d.outstanding_amount) < 0
),
self.references[0].precision("outstanding_amount") if self.references else None,
)
paid_amount = self.paid_amount if self.payment_type == "Receive" else self.received_amount
@@ -1448,6 +1454,13 @@ def get_outstanding_reference_documents(args):
condition += " and cost_center='%s'" % args.get("cost_center")
accounting_dimensions_filter.append(ple.cost_center == args.get("cost_center"))
# dynamic dimension filters
active_dimensions = get_dimensions()[0]
for dim in active_dimensions:
if args.get(dim.fieldname):
condition += " and {0}='{1}'".format(dim.fieldname, args.get(dim.fieldname))
accounting_dimensions_filter.append(ple[dim.fieldname] == args.get(dim.fieldname))
date_fields_dict = {
"posting_date": ["from_posting_date", "to_posting_date"],
"due_date": ["from_due_date", "to_due_date"],
@@ -1549,13 +1562,43 @@ def get_outstanding_reference_documents(args):
return data
def split_invoices_based_on_payment_terms(outstanding_invoices, company):
invoice_ref_based_on_payment_terms = {}
def split_invoices_based_on_payment_terms(outstanding_invoices, company) -> list:
"""Split a list of invoices based on their payment terms."""
exc_rates = get_currency_data(outstanding_invoices, company)
outstanding_invoices_after_split = []
for entry in outstanding_invoices:
if entry.voucher_type in ["Sales Invoice", "Purchase Invoice"]:
if payment_term_template := frappe.db.get_value(
entry.voucher_type, entry.voucher_no, "payment_terms_template"
):
split_rows = get_split_invoice_rows(entry, payment_term_template, exc_rates)
if not split_rows:
continue
if len(split_rows) > 1:
frappe.msgprint(
_("Splitting {0} {1} into {2} rows as per Payment Terms").format(
_(entry.voucher_type), frappe.bold(entry.voucher_no), len(split_rows)
),
alert=True,
)
outstanding_invoices_after_split += split_rows
continue
# If not an invoice or no payment terms template, add as it is
outstanding_invoices_after_split.append(entry)
return outstanding_invoices_after_split
def get_currency_data(outstanding_invoices: list, company: str = None) -> dict:
"""Get currency and conversion data for a list of invoices."""
exc_rates = frappe._dict()
company_currency = (
frappe.db.get_value("Company", company, "default_currency") if company else None
)
exc_rates = frappe._dict()
for doctype in ["Sales Invoice", "Purchase Invoice"]:
invoices = [x.voucher_no for x in outstanding_invoices if x.voucher_type == doctype]
for x in frappe.db.get_all(
@@ -1570,71 +1613,54 @@ def split_invoices_based_on_payment_terms(outstanding_invoices, company):
company_currency=company_currency,
)
for idx, d in enumerate(outstanding_invoices):
if d.voucher_type in ["Sales Invoice", "Purchase Invoice"]:
payment_term_template = frappe.db.get_value(
d.voucher_type, d.voucher_no, "payment_terms_template"
return exc_rates
def get_split_invoice_rows(invoice: dict, payment_term_template: str, exc_rates: dict) -> list:
"""Split invoice based on its payment schedule table."""
split_rows = []
allocate_payment_based_on_payment_terms = frappe.db.get_value(
"Payment Terms Template", payment_term_template, "allocate_payment_based_on_payment_terms"
)
if not allocate_payment_based_on_payment_terms:
return [invoice]
payment_schedule = frappe.get_all(
"Payment Schedule", filters={"parent": invoice.voucher_no}, fields=["*"], order_by="due_date"
)
for payment_term in payment_schedule:
if not payment_term.outstanding > 0.1:
continue
doc_details = exc_rates.get(payment_term.parent, None)
is_multi_currency_acc = (doc_details.currency != doc_details.company_currency) and (
doc_details.party_account_currency != doc_details.company_currency
)
payment_term_outstanding = flt(payment_term.outstanding)
if not is_multi_currency_acc:
payment_term_outstanding = doc_details.conversion_rate * flt(payment_term.outstanding)
split_rows.append(
frappe._dict(
{
"due_date": invoice.due_date,
"currency": invoice.currency,
"voucher_no": invoice.voucher_no,
"voucher_type": invoice.voucher_type,
"posting_date": invoice.posting_date,
"invoice_amount": flt(invoice.invoice_amount),
"outstanding_amount": payment_term_outstanding
if payment_term_outstanding
else invoice.outstanding_amount,
"payment_term_outstanding": payment_term_outstanding,
"payment_amount": payment_term.payment_amount,
"payment_term": payment_term.payment_term,
}
)
if payment_term_template:
allocate_payment_based_on_payment_terms = frappe.db.get_value(
"Payment Terms Template", payment_term_template, "allocate_payment_based_on_payment_terms"
)
if allocate_payment_based_on_payment_terms:
payment_schedule = frappe.get_all(
"Payment Schedule", filters={"parent": d.voucher_no}, fields=["*"]
)
)
for payment_term in payment_schedule:
if payment_term.outstanding > 0.1:
doc_details = exc_rates.get(payment_term.parent, None)
is_multi_currency_acc = (doc_details.currency != doc_details.company_currency) and (
doc_details.party_account_currency != doc_details.company_currency
)
payment_term_outstanding = flt(payment_term.outstanding)
if not is_multi_currency_acc:
payment_term_outstanding = doc_details.conversion_rate * flt(payment_term.outstanding)
invoice_ref_based_on_payment_terms.setdefault(idx, [])
invoice_ref_based_on_payment_terms[idx].append(
frappe._dict(
{
"due_date": d.due_date,
"currency": d.currency,
"voucher_no": d.voucher_no,
"voucher_type": d.voucher_type,
"posting_date": d.posting_date,
"invoice_amount": flt(d.invoice_amount),
"outstanding_amount": payment_term_outstanding
if payment_term_outstanding
else d.outstanding_amount,
"payment_term_outstanding": payment_term_outstanding,
"payment_amount": payment_term.payment_amount,
"payment_term": payment_term.payment_term,
}
)
)
outstanding_invoices_after_split = []
if invoice_ref_based_on_payment_terms:
for idx, ref in invoice_ref_based_on_payment_terms.items():
voucher_no = ref[0]["voucher_no"]
voucher_type = ref[0]["voucher_type"]
frappe.msgprint(
_("Spliting {} {} into {} row(s) as per Payment Terms").format(
voucher_type, voucher_no, len(ref)
),
alert=True,
)
outstanding_invoices_after_split += invoice_ref_based_on_payment_terms[idx]
existing_row = list(filter(lambda x: x.get("voucher_no") == voucher_no, outstanding_invoices))
index = outstanding_invoices.index(existing_row[0])
outstanding_invoices.pop(index)
outstanding_invoices_after_split += outstanding_invoices
return outstanding_invoices_after_split
return split_rows
def get_orders_to_be_billed(
@@ -1662,6 +1688,12 @@ def get_orders_to_be_billed(
if doc and hasattr(doc, "cost_center") and doc.cost_center:
condition = " and cost_center='%s'" % cost_center
# dynamic dimension filters
active_dimensions = get_dimensions()[0]
for dim in active_dimensions:
if filters.get(dim.fieldname):
condition += " and {0}='{1}'".format(dim.fieldname, filters.get(dim.fieldname))
if party_account_currency == company_currency:
grand_total_field = "base_grand_total"
rounded_total_field = "base_rounded_total"
@@ -1838,18 +1870,24 @@ def get_company_defaults(company):
def get_outstanding_on_journal_entry(name):
res = frappe.db.sql(
"SELECT "
'CASE WHEN party_type IN ("Customer") '
"THEN ifnull(sum(debit_in_account_currency - credit_in_account_currency), 0) "
"ELSE ifnull(sum(credit_in_account_currency - debit_in_account_currency), 0) "
"END as outstanding_amount "
"FROM `tabGL Entry` WHERE (voucher_no=%s OR against_voucher=%s) "
"AND party_type IS NOT NULL "
'AND party_type != ""',
(name, name),
as_dict=1,
)
gl = frappe.qb.DocType("GL Entry")
res = (
frappe.qb.from_(gl)
.select(
Case()
.when(
gl.party_type == "Customer",
Coalesce(Sum(gl.debit_in_account_currency - gl.credit_in_account_currency), 0),
)
.else_(Coalesce(Sum(gl.credit_in_account_currency - gl.debit_in_account_currency), 0))
.as_("outstanding_amount")
)
.where(
(Coalesce(gl.party_type, "") != "")
& (gl.is_cancelled == 0)
& ((gl.voucher_no == name) | (gl.against_voucher == name))
)
).run(as_dict=True)
outstanding_amount = res[0].get("outstanding_amount", 0) if res else 0

View File

@@ -1,15 +1,17 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import json
import unittest
import frappe
from frappe import qb
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import flt, nowdate
from frappe.utils import add_days, flt, nowdate
from erpnext.accounts.doctype.payment_entry.payment_entry import (
InvalidPaymentEntry,
get_outstanding_reference_documents,
get_payment_entry,
get_reference_details,
)
@@ -21,6 +23,7 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import (
create_sales_invoice,
create_sales_invoice_against_cost_center,
)
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.setup.doctype.employee.test_employee import make_employee
@@ -702,7 +705,50 @@ class TestPaymentEntry(FrappeTestCase):
pe2.submit()
# create return entry against si1
create_sales_invoice(is_return=1, return_against=si1.name, qty=-1)
cr_note = create_sales_invoice(is_return=1, return_against=si1.name, qty=-1)
si1_outstanding = frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount")
# create JE(credit note) manually against si1 and cr_note
je = frappe.get_doc(
{
"doctype": "Journal Entry",
"company": si1.company,
"voucher_type": "Credit Note",
"posting_date": nowdate(),
}
)
je.append(
"accounts",
{
"account": si1.debit_to,
"party_type": "Customer",
"party": si1.customer,
"debit": 0,
"credit": 100,
"debit_in_account_currency": 0,
"credit_in_account_currency": 100,
"reference_type": si1.doctype,
"reference_name": si1.name,
"cost_center": si1.items[0].cost_center,
},
)
je.append(
"accounts",
{
"account": cr_note.debit_to,
"party_type": "Customer",
"party": cr_note.customer,
"debit": 100,
"credit": 0,
"debit_in_account_currency": 100,
"credit_in_account_currency": 0,
"reference_type": cr_note.doctype,
"reference_name": cr_note.name,
"cost_center": cr_note.items[0].cost_center,
},
)
je.save().submit()
si1_outstanding = frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount")
self.assertEqual(si1_outstanding, -100)
@@ -1219,6 +1265,115 @@ class TestPaymentEntry(FrappeTestCase):
so.reload()
self.assertEqual(so.advance_paid, so.rounded_total)
def test_outstanding_invoices_api(self):
"""
Test if `get_outstanding_reference_documents` fetches invoices in the right order.
"""
customer = create_customer("Max Mustermann", "INR")
create_payment_terms_template()
# SI has an earlier due date and SI2 has a later due date
si = create_sales_invoice(
qty=1, rate=100, customer=customer, posting_date=add_days(nowdate(), -4)
)
si2 = create_sales_invoice(do_not_save=1, qty=1, rate=100, customer=customer)
si2.payment_terms_template = "Test Receivable Template"
si2.submit()
args = {
"posting_date": nowdate(),
"company": "_Test Company",
"party_type": "Customer",
"payment_type": "Pay",
"party": customer,
"party_account": "Debtors - _TC",
}
args.update(
{
"get_outstanding_invoices": True,
"from_posting_date": add_days(nowdate(), -4),
"to_posting_date": add_days(nowdate(), 2),
}
)
references = get_outstanding_reference_documents(args)
self.assertEqual(len(references), 3)
self.assertEqual(references[0].voucher_no, si.name)
self.assertEqual(references[1].voucher_no, si2.name)
self.assertEqual(references[2].voucher_no, si2.name)
self.assertEqual(references[1].payment_term, "Basic Amount Receivable")
self.assertEqual(references[2].payment_term, "Tax Receivable")
def test_partial_cancel_for_payment_entry(self):
si = create_sales_invoice()
pe = get_payment_entry(si.doctype, si.name)
pe.save()
pe.submit()
# Additional GL Entry
tax_amount = 10
reference_row = pe.references[0]
gl_args = {
"party_type": pe.party_type,
"party": pe.party,
"against_voucher_type": reference_row.reference_doctype,
"against_voucher": reference_row.reference_name,
"voucher_detail_no": reference_row.name,
}
gl_dicts = []
gl_dicts.extend(
[
pe.get_gl_dict(
{
"account": pe.paid_to,
"credit": tax_amount,
"credit_in_account_currency": tax_amount,
**gl_args,
}
),
pe.get_gl_dict(
{
"account": pe.paid_from,
"debit": tax_amount,
"debit_in_account_currency": tax_amount,
**gl_args,
}
),
]
)
make_gl_entries(gl_dicts)
# Assert PLEs Before
self.assertPLEntries(
pe,
[
{"amount": -100.0, "against_voucher_no": si.name},
{"amount": 10.0, "against_voucher_no": si.name},
],
)
# Partially cancel Payment Entry
make_reverse_gl_entries(gl_dicts, partial_cancel=True)
self.assertPLEntries(pe, [{"amount": -100.0, "against_voucher_no": si.name}])
def assertPLEntries(self, payment_doc, expected_pl_entries):
pl_entries = frappe.get_all(
"Payment Ledger Entry",
filters={
"voucher_type": payment_doc.doctype,
"voucher_no": payment_doc.name,
"delinked": 0,
},
fields=["amount", "against_voucher_no"],
)
out_str = json.dumps(sorted(pl_entries, key=json.dumps))
expected_out_str = json.dumps(sorted(expected_pl_entries, key=json.dumps))
self.assertEqual(out_str, expected_out_str)
def create_payment_entry(**args):
payment_entry = frappe.new_doc("Payment Entry")
@@ -1279,6 +1434,9 @@ def create_payment_terms_template():
def create_payment_terms_template_with_discount(
name=None, discount_type=None, discount=None, template_name=None
):
"""
Create a Payment Terms Template with % or amount discount.
"""
create_payment_term(name or "30 Credit Days with 10% Discount")
template_name = template_name or "Test Discount Template"

View File

@@ -294,7 +294,7 @@ class TestPaymentLedgerEntry(FrappeTestCase):
cr_note1.return_against = si3.name
cr_note1 = cr_note1.save().submit()
pl_entries = (
pl_entries_si3 = (
qb.from_(ple)
.select(
ple.voucher_type,
@@ -309,7 +309,24 @@ class TestPaymentLedgerEntry(FrappeTestCase):
.run(as_dict=True)
)
expected_values = [
pl_entries_cr_note1 = (
qb.from_(ple)
.select(
ple.voucher_type,
ple.voucher_no,
ple.against_voucher_type,
ple.against_voucher_no,
ple.amount,
ple.delinked,
)
.where(
(ple.against_voucher_type == cr_note1.doctype) & (ple.against_voucher_no == cr_note1.name)
)
.orderby(ple.creation)
.run(as_dict=True)
)
expected_values_for_si3 = [
{
"voucher_type": si3.doctype,
"voucher_no": si3.name,
@@ -317,18 +334,21 @@ class TestPaymentLedgerEntry(FrappeTestCase):
"against_voucher_no": si3.name,
"amount": amount,
"delinked": 0,
},
}
]
# credit/debit notes post ledger entries against itself
expected_values_for_cr_note1 = [
{
"voucher_type": cr_note1.doctype,
"voucher_no": cr_note1.name,
"against_voucher_type": si3.doctype,
"against_voucher_no": si3.name,
"against_voucher_type": cr_note1.doctype,
"against_voucher_no": cr_note1.name,
"amount": -amount,
"delinked": 0,
},
]
self.assertEqual(pl_entries[0], expected_values[0])
self.assertEqual(pl_entries[1], expected_values[1])
self.assertEqual(pl_entries_si3, expected_values_for_si3)
self.assertEqual(pl_entries_cr_note1, expected_values_for_cr_note1)
def test_je_against_inv_and_note(self):
ple = self.ple

View File

@@ -83,6 +83,8 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
this.frm.change_custom_button_type('Allocate', null, 'default');
}
this.frm.trigger("set_query_for_dimension_filters");
// check for any running reconciliation jobs
if (this.frm.doc.receivable_payable_account) {
this.frm.call({
@@ -113,6 +115,25 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
}
}
set_query_for_dimension_filters() {
frappe.call({
method: "erpnext.accounts.doctype.payment_reconciliation.payment_reconciliation.get_queries_for_dimension_filters",
args: {
company: this.frm.doc.company,
},
callback: (r) => {
if (!r.exc && r.message) {
r.message.forEach(x => {
this.frm.set_query(x.fieldname, () => {
return {
'filters': x.filters
};
});
});
}
}
});
}
company() {
this.frm.set_value('party', '');

View File

@@ -24,7 +24,9 @@
"invoice_limit",
"payment_limit",
"bank_cash_account",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"sec_break1",
"invoice_name",
"invoices",
@@ -199,13 +201,26 @@
"fieldname": "payment_name",
"fieldtype": "Data",
"label": "Filter on Payment"
},
{
"collapsible": 1,
"collapsible_depends_on": "eval: doc.invoices.length == 0",
"depends_on": "eval:doc.receivable_payable_account",
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
"label": "Accounting Dimensions Filter"
},
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
}
],
"hide_toolbar": 1,
"icon": "icon-resize-horizontal",
"is_virtual": 1,
"issingle": 1,
"links": [],
"modified": "2023-08-15 05:35:50.109290",
"modified": "2023-12-14 13:38:16.264013",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation",
@@ -230,6 +245,5 @@
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
"states": []
}

View File

@@ -10,6 +10,7 @@ from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt, fmt_money, get_link_to_form, getdate, nowdate, today
import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions
from erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation import (
is_any_doc_running,
)
@@ -28,6 +29,59 @@ class PaymentReconciliation(Document):
self.common_filter_conditions = []
self.accounting_dimension_filter_conditions = []
self.ple_posting_date_filter = []
self.dimensions = get_dimensions()[0]
def load_from_db(self):
# 'modified' attribute is required for `run_doc_method` to work properly.
doc_dict = frappe._dict(
{
"modified": None,
"company": None,
"party": None,
"party_type": None,
"receivable_payable_account": None,
"default_advance_account": None,
"from_invoice_date": None,
"to_invoice_date": None,
"invoice_limit": 50,
"from_payment_date": None,
"to_payment_date": None,
"payment_limit": 50,
"minimum_invoice_amount": None,
"minimum_payment_amount": None,
"maximum_invoice_amount": None,
"maximum_payment_amount": None,
"bank_cash_account": None,
"cost_center": None,
"payment_name": None,
"invoice_name": None,
}
)
super(Document, self).__init__(doc_dict)
def save(self):
return
@staticmethod
def get_list(args):
pass
@staticmethod
def get_count(args):
pass
@staticmethod
def get_stats(args):
pass
def db_insert(self, *args, **kwargs):
pass
def db_update(self, *args, **kwargs):
pass
def delete(self):
pass
@frappe.whitelist()
def get_unreconciled_entries(self):
@@ -58,9 +112,7 @@ class PaymentReconciliation(Document):
def get_payment_entries(self):
order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order"
condition = self.get_conditions(get_payments=True)
if self.payment_name:
condition += "name like '%%{0}%%'".format(self.payment_name)
condition = self.get_payment_entry_conditions()
payment_entries = get_advance_payment_entries_for_regional(
self.party_type,
@@ -70,71 +122,73 @@ class PaymentReconciliation(Document):
against_all_orders=True,
limit=self.payment_limit,
condition=condition,
payment_name=self.payment_name,
)
return payment_entries
def get_jv_entries(self):
condition = self.get_conditions()
je = qb.DocType("Journal Entry")
jea = qb.DocType("Journal Entry Account")
conditions = self.get_journal_filter_conditions()
# Dimension filters
for x in self.dimensions:
dimension = x.fieldname
if self.get(dimension):
conditions.append(jea[dimension] == self.get(dimension))
if self.payment_name:
condition += f" and t1.name like '%%{self.payment_name}%%'"
conditions.append(je.name.like(f"%%{self.payment_name}%%"))
if self.get("cost_center"):
condition += f" and t2.cost_center = '{self.cost_center}' "
conditions.append(jea.cost_center == self.cost_center)
dr_or_cr = (
"credit_in_account_currency"
if erpnext.get_party_account_type(self.party_type) == "Receivable"
else "debit_in_account_currency"
)
conditions.append(jea[dr_or_cr].gt(0))
bank_account_condition = (
"t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1"
if self.bank_cash_account:
conditions.append(jea.against_account.like(f"%%{self.bank_cash_account}%%"))
journal_query = (
qb.from_(je)
.inner_join(jea)
.on(jea.parent == je.name)
.select(
ConstantColumn("Journal Entry").as_("reference_type"),
je.name.as_("reference_name"),
je.posting_date,
je.remark.as_("remarks"),
jea.name.as_("reference_row"),
jea[dr_or_cr].as_("amount"),
jea.is_advance,
jea.exchange_rate,
jea.account_currency.as_("currency"),
jea.cost_center.as_("cost_center"),
)
.where(
(je.docstatus == 1)
& (jea.party_type == self.party_type)
& (jea.party == self.party)
& (jea.account == self.receivable_payable_account)
& (
(jea.reference_type == "")
| (jea.reference_type.isnull())
| (jea.reference_type.isin(("Sales Order", "Purchase Order")))
)
)
.where(Criterion.all(conditions))
.orderby(je.posting_date)
)
limit = f"limit {self.payment_limit}" if self.payment_limit else " "
if self.payment_limit:
journal_query = journal_query.limit(self.payment_limit)
# 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, t2.exchange_rate,
t2.account_currency as currency, t2.cost_center as cost_center
from
`tabJournal Entry` t1, `tabJournal Entry Account` t2
where
t1.name = t2.parent and t1.docstatus = 1 and t2.docstatus = 1
and t2.party_type = %(party_type)s and t2.party = %(party)s
and t2.account = %(account)s and {dr_or_cr} > 0 {condition}
and (t2.reference_type is null or t2.reference_type = '' or
(t2.reference_type in ('Sales Order', 'Purchase Order')
and t2.reference_name is not null and t2.reference_name != ''))
and (CASE
WHEN t1.voucher_type in ('Debit Note', 'Credit Note')
THEN 1=1
ELSE {bank_account_condition}
END)
order by t1.posting_date
{limit}
""".format(
**{
"dr_or_cr": dr_or_cr,
"bank_account_condition": bank_account_condition,
"condition": condition,
"limit": limit,
}
),
{
"party_type": self.party_type,
"party": self.party,
"account": self.receivable_payable_account,
"bank_cash_account": "%%%s%%" % self.bank_cash_account,
},
as_dict=1,
)
journal_entries = journal_query.run(as_dict=True)
return list(journal_entries)
@@ -177,20 +231,18 @@ class PaymentReconciliation(Document):
self.common_filter_conditions.append(ple.account == self.receivable_payable_account)
self.get_return_invoices()
return_invoices = [
x for x in self.return_invoices if x.return_against == None or x.return_against == ""
]
outstanding_dr_or_cr = []
if return_invoices:
if self.return_invoices:
ple_query = QueryPaymentLedger()
return_outstanding = ple_query.get_voucher_outstandings(
vouchers=return_invoices,
vouchers=self.return_invoices,
common_filter=self.common_filter_conditions,
posting_date=self.ple_posting_date_filter,
min_outstanding=-(self.minimum_payment_amount) if self.minimum_payment_amount else None,
max_outstanding=-(self.maximum_payment_amount) if self.maximum_payment_amount else None,
get_payments=True,
accounting_dimensions=self.accounting_dimension_filter_conditions,
)
for inv in return_outstanding:
@@ -340,8 +392,15 @@ class PaymentReconciliation(Document):
row = self.append("allocation", {})
row.update(entry)
def update_dimension_values_in_allocated_entries(self, res):
for x in self.dimensions:
dimension = x.fieldname
if self.get(dimension):
res[dimension] = self.get(dimension)
return res
def get_allocated_entry(self, pay, inv, allocated_amount):
return frappe._dict(
res = frappe._dict(
{
"reference_type": pay.get("reference_type"),
"reference_name": pay.get("reference_name"),
@@ -357,6 +416,9 @@ class PaymentReconciliation(Document):
}
)
res = self.update_dimension_values_in_allocated_entries(res)
return res
def reconcile_allocations(self, skip_ref_details_update_for_pe=False):
adjust_allocations_for_taxes(self)
dr_or_cr = (
@@ -379,10 +441,10 @@ class PaymentReconciliation(Document):
reconciled_entry.append(payment_details)
if entry_list:
reconcile_against_document(entry_list, skip_ref_details_update_for_pe)
reconcile_against_document(entry_list, skip_ref_details_update_for_pe, self.dimensions)
if dr_or_cr_notes:
reconcile_dr_cr_note(dr_or_cr_notes, self.company)
reconcile_dr_cr_note(dr_or_cr_notes, self.company, self.dimensions)
@frappe.whitelist()
def reconcile(self):
@@ -411,7 +473,7 @@ class PaymentReconciliation(Document):
self.get_unreconciled_entries()
def get_payment_details(self, row, dr_or_cr):
return frappe._dict(
payment_details = frappe._dict(
{
"voucher_type": row.get("reference_type"),
"voucher_no": row.get("reference_name"),
@@ -434,6 +496,12 @@ class PaymentReconciliation(Document):
}
)
for x in self.dimensions:
if row.get(x.fieldname):
payment_details[x.fieldname] = row.get(x.fieldname)
return payment_details
def check_mandatory_to_fetch(self):
for fieldname in ["company", "party_type", "party", "receivable_payable_account"]:
if not self.get(fieldname):
@@ -487,6 +555,27 @@ class PaymentReconciliation(Document):
invoice_exchange_map.update(purchase_invoice_map)
journals = [
d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Journal Entry"
]
journals.extend(
[d.get("reference_name") for d in payments if d.get("reference_type") == "Journal Entry"]
)
if journals:
journals = list(set(journals))
journals_map = frappe._dict(
frappe.db.get_all(
"Journal Entry Account",
filters={"parent": ("in", journals), "account": ("in", [self.receivable_payable_account])},
fields=[
"parent as `name`",
"exchange_rate",
],
as_list=1,
)
)
invoice_exchange_map.update(journals_map)
return invoice_exchange_map
def validate_allocation(self):
@@ -520,6 +609,13 @@ class PaymentReconciliation(Document):
if not invoices_to_reconcile:
frappe.throw(_("No records found in Allocation table"))
def build_dimensions_filter_conditions(self):
ple = qb.DocType("Payment Ledger Entry")
for x in self.dimensions:
dimension = x.fieldname
if self.get(dimension):
self.accounting_dimension_filter_conditions.append(ple[dimension] == self.get(dimension))
def build_qb_filter_conditions(self, get_invoices=False, get_return_invoices=False):
self.common_filter_conditions.clear()
self.accounting_dimension_filter_conditions.clear()
@@ -543,40 +639,58 @@ class PaymentReconciliation(Document):
if self.to_payment_date:
self.ple_posting_date_filter.append(ple.posting_date.lte(self.to_payment_date))
def get_conditions(self, get_payments=False):
condition = " and company = '{0}' ".format(self.company)
self.build_dimensions_filter_conditions()
if self.get("cost_center") and get_payments:
condition = " and cost_center = '{0}' ".format(self.cost_center)
def get_payment_entry_conditions(self):
conditions = []
pe = qb.DocType("Payment Entry")
conditions.append(pe.company == self.company)
condition += (
" and posting_date >= {0}".format(frappe.db.escape(self.from_payment_date))
if self.from_payment_date
else ""
)
condition += (
" and posting_date <= {0}".format(frappe.db.escape(self.to_payment_date))
if self.to_payment_date
else ""
)
if self.get("cost_center"):
conditions.append(pe.cost_center == self.cost_center)
if self.from_payment_date:
conditions.append(pe.posting_date.gte(self.from_payment_date))
if self.to_payment_date:
conditions.append(pe.posting_date.lte(self.to_payment_date))
if self.minimum_payment_amount:
condition += (
" and unallocated_amount >= {0}".format(flt(self.minimum_payment_amount))
if get_payments
else " and total_debit >= {0}".format(flt(self.minimum_payment_amount))
)
conditions.append(pe.unallocated_amount.gte(flt(self.minimum_payment_amount)))
if self.maximum_payment_amount:
condition += (
" and unallocated_amount <= {0}".format(flt(self.maximum_payment_amount))
if get_payments
else " and total_debit <= {0}".format(flt(self.maximum_payment_amount))
)
conditions.append(pe.unallocated_amount.lte(flt(self.maximum_payment_amount)))
return condition
# pass dynamic dimension filter values to payment query
for x in self.dimensions:
dimension = x.fieldname
if self.get(dimension):
conditions.append(pe[dimension] == self.get(dimension))
return conditions
def get_journal_filter_conditions(self):
conditions = []
je = qb.DocType("Journal Entry")
jea = qb.DocType("Journal Entry Account")
conditions.append(je.company == self.company)
if self.from_payment_date:
conditions.append(je.posting_date.gte(self.from_payment_date))
if self.to_payment_date:
conditions.append(je.posting_date.lte(self.to_payment_date))
if self.minimum_payment_amount:
conditions.append(je.total_debit.gte(self.minimum_payment_amount))
if self.maximum_payment_amount:
conditions.append(je.total_debit.lte(self.maximum_payment_amount))
return conditions
def reconcile_dr_cr_note(dr_cr_notes, company):
def reconcile_dr_cr_note(dr_cr_notes, company, active_dimensions=None):
for inv in dr_cr_notes:
voucher_type = "Credit Note" if inv.voucher_type == "Sales Invoice" else "Debit Note"
@@ -626,6 +740,15 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
}
)
# Credit Note(JE) will inherit the same dimension values as payment
dimensions_dict = frappe._dict()
if active_dimensions:
for dim in active_dimensions:
dimensions_dict[dim.fieldname] = inv.get(dim.fieldname)
jv.accounts[0].update(dimensions_dict)
jv.accounts[1].update(dimensions_dict)
jv.flags.ignore_mandatory = True
jv.flags.skip_remarks_creation = True
jv.flags.ignore_exchange_rate = True
@@ -659,9 +782,27 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
inv.against_voucher,
None,
inv.cost_center,
dimensions_dict,
)
@erpnext.allow_regional
def adjust_allocations_for_taxes(doc):
pass
@frappe.whitelist()
def get_queries_for_dimension_filters(company: str = None):
dimensions_with_filters = []
for d in get_dimensions()[0]:
filters = {}
meta = frappe.get_meta(d.document_type)
if meta.has_field("company") and company:
filters.update({"company": company})
if meta.is_tree:
filters.update({"is_group": 0})
dimensions_with_filters.append({"fieldname": d.fieldname, "filters": filters})
return dimensions_with_filters

View File

@@ -1137,6 +1137,40 @@ class TestPaymentReconciliation(FrappeTestCase):
self.assertEqual(pay.unallocated_amount, 1000)
self.assertEqual(pay.difference_amount, 0)
def test_rounding_of_unallocated_amount(self):
self.supplier = "_Test Supplier USD"
pi = self.create_purchase_invoice(qty=1, rate=10, do_not_submit=True)
pi.supplier = self.supplier
pi.currency = "USD"
pi.conversion_rate = 80
pi.credit_to = self.creditors_usd
pi.save().submit()
pe = get_payment_entry(pi.doctype, pi.name)
pe.target_exchange_rate = 78.726500000
pe.received_amount = 26.75
pe.paid_amount = 2105.93
pe.references = []
pe.save().submit()
# unallocated_amount will have some rounding loss - 26.749950
self.assertNotEqual(pe.unallocated_amount, 26.75)
pr = frappe.get_doc("Payment Reconciliation")
pr.company = self.company
pr.party_type = "Supplier"
pr.party = self.supplier
pr.receivable_payable_account = self.creditors_usd
pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate()
pr.get_unreconciled_entries()
invoices = [invoice.as_dict() for invoice in pr.invoices]
payments = [payment.as_dict() for payment in pr.payments]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
# Should not raise frappe.exceptions.ValidationError: Payment Entry has been modified after you pulled it. Please pull it again.
pr.reconcile()
def make_customer(customer_name, currency=None):
if not frappe.db.exists("Customer", customer_name):

View File

@@ -24,7 +24,9 @@
"difference_account",
"exchange_rate",
"currency",
"cost_center"
"accounting_dimensions_section",
"cost_center",
"dimension_col_break"
],
"fields": [
{
@@ -157,11 +159,21 @@
"fieldname": "gain_loss_posting_date",
"fieldtype": "Date",
"label": "Difference Posting Date"
},
{
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
"label": "Accounting Dimensions"
},
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
}
],
"is_virtual": 1,
"istable": 1,
"links": [],
"modified": "2023-10-23 10:44:56.066303",
"modified": "2023-12-14 13:38:26.104150",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Allocation",

View File

@@ -6,4 +6,6 @@ from frappe.model.document import Document
class PaymentReconciliationAllocation(Document):
pass
@staticmethod
def get_list(args):
pass

View File

@@ -71,9 +71,10 @@
"label": "Exchange Rate"
}
],
"is_virtual": 1,
"istable": 1,
"links": [],
"modified": "2022-11-08 18:18:02.502149",
"modified": "2023-11-17 17:33:45.455166",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Invoice",

View File

@@ -6,4 +6,6 @@ from frappe.model.document import Document
class PaymentReconciliationInvoice(Document):
pass
@staticmethod
def get_list(args):
pass

View File

@@ -107,9 +107,10 @@
"options": "Cost Center"
}
],
"is_virtual": 1,
"istable": 1,
"links": [],
"modified": "2023-09-03 07:43:29.965353",
"modified": "2023-11-17 17:33:34.818530",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Payment",

View File

@@ -6,4 +6,6 @@ from frappe.model.document import Document
class PaymentReconciliationPayment(Document):
pass
@staticmethod
def get_list(args):
pass

View File

@@ -18,6 +18,7 @@
"is_pos",
"is_return",
"update_billed_amount_in_sales_order",
"update_billed_amount_in_delivery_note",
"column_break1",
"company",
"posting_date",
@@ -1549,12 +1550,19 @@
"fieldtype": "Currency",
"label": "Amount Eligible for Commission",
"read_only": 1
},
{
"default": "1",
"depends_on": "eval: doc.is_return && doc.return_against",
"fieldname": "update_billed_amount_in_delivery_note",
"fieldtype": "Check",
"label": "Update Billed Amount in Delivery Note"
}
],
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
"modified": "2022-09-30 03:49:50.455199",
"modified": "2023-11-20 12:27:12.848149",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice",

View File

@@ -13,7 +13,6 @@ from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_lo
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
SalesInvoice,
get_bank_cash_account,
get_mode_of_payment_info,
update_multi_mode_option,
)
@@ -52,7 +51,6 @@ class POSInvoice(SalesInvoice):
self.validate_stock_availablility()
self.validate_return_items_qty()
self.set_status()
self.set_account_for_mode_of_payment()
self.validate_pos()
self.validate_payment_amount()
self.validate_loyalty_transaction()
@@ -584,11 +582,6 @@ class POSInvoice(SalesInvoice):
update_multi_mode_option(self, pos_profile)
self.paid_amount = 0
def set_account_for_mode_of_payment(self):
for pay in self.payments:
if not pay.account:
pay.account = get_bank_cash_account(pay.mode_of_payment, self.company).get("account")
@frappe.whitelist()
def create_payment_request(self):
for pay in self.payments:
@@ -659,7 +652,7 @@ def get_stock_availability(item_code, warehouse):
return bin_qty - pos_sales_qty, is_stock_item
else:
is_stock_item = True
if frappe.db.exists("Product Bundle", item_code):
if frappe.db.exists("Product Bundle", {"name": item_code, "disabled": 0}):
return get_bundle_availability(item_code, warehouse), is_stock_item
else:
is_stock_item = False
@@ -705,7 +698,7 @@ def get_pos_reserved_qty(item_code, warehouse):
reserved_qty = (
frappe.qb.from_(p_inv)
.from_(p_item)
.select(Sum(p_item.qty).as_("qty"))
.select(Sum(p_item.stock_qty).as_("stock_qty"))
.where(
(p_inv.name == p_item.parent)
& (IfNull(p_inv.consolidated_invoice, "") == "")
@@ -716,7 +709,7 @@ def get_pos_reserved_qty(item_code, warehouse):
)
).run(as_dict=True)
return reserved_qty[0].qty or 0 if reserved_qty else 0
return flt(reserved_qty[0].stock_qty) if reserved_qty else 0
@frappe.whitelist()

View File

@@ -521,7 +521,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None):
values.extend(warehouses)
if items:
condition = " and `tab{child_doc}`.{apply_on} in ({items})".format(
condition += " and `tab{child_doc}`.{apply_on} in ({items})".format(
child_doc=child_doctype, apply_on=apply_on, items=",".join(["%s"] * len(items))
)

View File

@@ -415,7 +415,7 @@ def reconcile(doc: None | str = None) -> None:
# Update the parent doc about the exception
frappe.db.rollback()
traceback = frappe.get_traceback()
traceback = frappe.get_traceback(with_context=True)
if traceback:
message = "Traceback: <br>" + traceback
frappe.db.set_value("Process Payment Reconciliation Log", log, "error_log", message)

View File

@@ -15,6 +15,7 @@
"group_by",
"cost_center",
"territory",
"ignore_exchange_rate_revaluation_journals",
"column_break_14",
"to_date",
"finance_book",
@@ -374,10 +375,16 @@
"fieldname": "pdf_name",
"fieldtype": "Data",
"label": "PDF Name"
},
{
"default": "0",
"fieldname": "ignore_exchange_rate_revaluation_journals",
"fieldtype": "Check",
"label": "Ignore Exchange Rate Revaluation Journals"
}
],
"links": [],
"modified": "2023-08-28 12:59:53.071334",
"modified": "2023-12-18 12:20:08.965120",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Statement Of Accounts",

View File

@@ -64,6 +64,18 @@ def get_statement_dict(doc, get_statement_dict=False):
statement_dict = {}
ageing = ""
err_journals = None
if doc.report == "General Ledger" and doc.ignore_exchange_rate_revaluation_journals:
err_journals = frappe.db.get_all(
"Journal Entry",
filters={
"company": doc.company,
"docstatus": 1,
"voucher_type": ("in", ["Exchange Rate Revaluation", "Exchange Gain Or Loss"]),
},
as_list=True,
)
for entry in doc.customers:
if doc.include_ageing:
ageing = set_ageing(doc, entry)
@@ -76,6 +88,8 @@ def get_statement_dict(doc, get_statement_dict=False):
)
filters = get_common_filters(doc)
if err_journals:
filters.update({"voucher_no_not_in": [x[0] for x in err_journals]})
if doc.report == "General Ledger":
filters.update(get_gl_filters(doc, entry, tax_id, presentation_currency))

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', "Repost Payment Ledger", "Repost Accounting Ledger"];
this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"];
if(!this.frm.doc.__islocal) {
// show credit_to in print format
@@ -99,8 +99,7 @@ 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) {
if(doc.docstatus == 1 && doc.outstanding_amount != 0 && !doc.on_hold) {
this.frm.add_custom_button(
__('Payment'),
() => this.make_payment_entry(),
@@ -164,6 +163,18 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
}
})
}, __("Get Items From"));
if (!this.frm.doc.is_return) {
frappe.db.get_single_value("Buying Settings", "maintain_same_rate").then((value) => {
if (value) {
this.frm.doc.items.forEach((item) => {
this.frm.fields_dict.items.grid.update_docfield_property(
"rate", "read_only", (item.purchase_receipt && item.pr_detail)
);
});
}
});
}
}
this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted);
@@ -181,7 +192,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
}
this.frm.set_df_property("tax_withholding_category", "hidden", doc.apply_tds ? 0 : 1);
erpnext.accounts.unreconcile_payments.add_unreconcile_btn(me.frm);
erpnext.accounts.unreconcile_payment.add_unreconcile_btn(me.frm);
}
unblock_invoice() {

View File

@@ -13,6 +13,7 @@ from erpnext.accounts.deferred_revenue import validate_service_stop_date
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
validate_docs_for_deferred_accounting,
validate_docs_for_voucher_types,
)
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
check_if_return_invoice_linked_with_payment_entry,
@@ -130,6 +131,18 @@ class PurchaseInvoice(BuyingController):
self.reset_default_field_value("set_warehouse", "items", "warehouse")
self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
self.set_percentage_received()
def set_percentage_received(self):
total_billed_qty = 0.0
total_received_qty = 0.0
for row in self.items:
if row.purchase_receipt and row.pr_detail and row.received_qty:
total_billed_qty += row.qty
total_received_qty += row.received_qty
if total_billed_qty and total_received_qty:
self.per_received = total_received_qty / total_billed_qty * 100
def validate_release_date(self):
if self.release_date and getdate(nowdate()) >= getdate(self.release_date):
@@ -494,6 +507,7 @@ class PurchaseInvoice(BuyingController):
def validate_for_repost(self):
self.validate_write_off_account()
self.validate_expense_account()
validate_docs_for_voucher_types(["Purchase Invoice"])
validate_docs_for_deferred_accounting([], [self.name])
def on_submit(self):
@@ -532,7 +546,11 @@ class PurchaseInvoice(BuyingController):
if self.update_stock == 1:
self.repost_future_sle_and_gle()
self.update_project()
if (
frappe.db.get_single_value("Buying Settings", "project_update_frequency") == "Each Transaction"
):
self.update_project()
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
self.update_advance_tax_references()
@@ -653,9 +671,7 @@ class PurchaseInvoice(BuyingController):
"credit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
else grand_total,
"against_voucher": self.return_against
if cint(self.is_return) and self.return_against
else self.name,
"against_voucher": self.name,
"against_voucher_type": self.doctype,
"project": self.project,
"cost_center": self.cost_center,
@@ -919,17 +935,6 @@ class PurchaseInvoice(BuyingController):
item=item,
)
)
# update gross amount of asset bought through this document
assets = frappe.db.get_all(
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
)
for asset in assets:
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
frappe.db.set_value(
"Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)
)
if (
self.auto_accounting_for_stock
and self.is_opening == "No"
@@ -969,12 +974,25 @@ class PurchaseInvoice(BuyingController):
item.item_tax_amount, item.precision("item_tax_amount")
)
if item.is_fixed_asset and item.landed_cost_voucher_amount:
self.update_gross_purchase_amount_for_linked_assets(item)
def update_gross_purchase_amount_for_linked_assets(self, item):
assets = frappe.db.get_all(
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
"Asset",
filters={"purchase_invoice": self.name, "item_code": item.item_code},
fields=["name", "asset_quantity"],
)
for asset in assets:
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate))
purchase_amount = flt(item.valuation_rate) * asset.asset_quantity
frappe.db.set_value(
"Asset",
asset.name,
{
"gross_purchase_amount": purchase_amount,
"purchase_receipt_amount": purchase_amount,
},
)
def make_stock_adjustment_entry(
self, gl_entries, item, voucher_wise_stock_value, account_currency
@@ -1269,7 +1287,10 @@ class PurchaseInvoice(BuyingController):
if self.update_stock == 1:
self.repost_future_sle_and_gle()
self.update_project()
if (
frappe.db.get_single_value("Buying Settings", "project_update_frequency") == "Each Transaction"
):
self.update_project()
self.db_set("status", "Cancelled")
unlink_inter_company_doc(self.doctype, self.name, self.inter_company_invoice_reference)
@@ -1281,19 +1302,29 @@ class PurchaseInvoice(BuyingController):
"Repost Payment Ledger Items",
"Repost Accounting Ledger",
"Repost Accounting Ledger Items",
"Unreconcile Payment",
"Unreconcile Payment Entries",
"Payment Ledger Entry",
"Tax Withheld Vouchers",
)
self.update_advance_tax_references(cancel=1)
def update_project(self):
project_list = []
projects = frappe._dict()
for d in self.items:
if d.project and d.project not in project_list:
project = frappe.get_doc("Project", d.project)
project.update_purchase_costing()
project.db_update()
project_list.append(d.project)
if d.project:
if self.docstatus == 1:
projects[d.project] = projects.get(d.project, 0) + d.base_net_amount
elif self.docstatus == 2:
projects[d.project] = projects.get(d.project, 0) - d.base_net_amount
pj = frappe.qb.DocType("Project")
for proj, value in projects.items():
res = (
frappe.qb.from_(pj).select(pj.total_purchase_cost).where(pj.name == proj).for_update().run()
)
current_purchase_cost = res and res[0][0] or 0
frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value)
def validate_supplier_invoice(self):
if self.bill_date:
@@ -1507,12 +1538,8 @@ class PurchaseInvoice(BuyingController):
elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
self.status = "Unpaid"
# Check if outstanding amount is 0 due to debit note issued against invoice
elif (
outstanding_amount <= 0
and self.is_return == 0
and frappe.db.get_value(
"Purchase Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1}
)
elif self.is_return == 0 and frappe.db.get_value(
"Purchase Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1}
):
self.status = "Debit Note Issued"
elif self.is_return == 1:
@@ -1641,10 +1668,6 @@ def make_inter_company_sales_invoice(source_name, target_doc=None):
return make_inter_company_transaction("Purchase Invoice", source_name, target_doc)
def on_doctype_update():
frappe.db.add_index("Purchase Invoice", ["supplier", "is_return", "return_against"])
@frappe.whitelist()
def make_purchase_receipt(source_name, target_doc=None):
def update_item(obj, target, source_parent):
@@ -1686,6 +1709,4 @@ def make_purchase_receipt(source_name, target_doc=None):
target_doc,
)
doc.set_onload("ignore_price_list", True)
return doc

View File

@@ -1893,6 +1893,21 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
self.assertEqual(pi.items[0].cost_center, "_Test Cost Center Buying - _TC")
def test_debit_note_with_account_mismatch(self):
new_creditors = create_account(
parent_account="Accounts Payable - _TC",
account_name="Creditors 2",
company="_Test Company",
account_type="Payable",
)
pi = make_purchase_invoice(qty=1, rate=1000)
dr_note = make_purchase_invoice(
qty=-1, rate=1000, is_return=1, return_against=pi.name, do_not_save=True
)
dr_note.credit_to = new_creditors
self.assertRaises(frappe.ValidationError, dr_note.save)
def check_gl_entries(
doc,

View File

@@ -492,6 +492,7 @@
"fieldtype": "Column Break"
},
{
"allow_on_submit": 1,
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
@@ -499,6 +500,7 @@
"print_hide": 1
},
{
"allow_on_submit": 1,
"default": ":Company",
"depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "cost_center",
@@ -891,7 +893,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2023-11-14 18:33:48.547297",
"modified": "2023-12-25 22:00:28.043555",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",

View File

@@ -126,7 +126,7 @@
"fieldname": "rate",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Rate",
"label": "Tax Rate",
"oldfieldname": "rate",
"oldfieldtype": "Currency"
},
@@ -230,7 +230,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2021-08-05 20:04:36.618240",
"modified": "2024-01-14 10:04:36.618240",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Taxes and Charges",
@@ -239,4 +239,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}
}

View File

@@ -10,12 +10,7 @@ from frappe.utils.data import comma_and
class RepostAccountingLedger(Document):
def __init__(self, *args, **kwargs):
super(RepostAccountingLedger, self).__init__(*args, **kwargs)
self._allowed_types = [
x.document_type
for x in frappe.db.get_all(
"Repost Allowed Types", filters={"allowed": True}, fields=["distinct(document_type)"]
)
]
self._allowed_types = get_allowed_types_from_settings()
def validate(self):
self.validate_vouchers()
@@ -56,15 +51,7 @@ class RepostAccountingLedger(Document):
def validate_vouchers(self):
if self.vouchers:
# Validate voucher types
voucher_types = set([x.voucher_type for x in self.vouchers])
if disallowed_types := voucher_types.difference(self._allowed_types):
frappe.throw(
_("{0} types are not allowed. Only {1} are.").format(
frappe.bold(comma_and(list(disallowed_types))),
frappe.bold(comma_and(list(self._allowed_types))),
)
)
validate_docs_for_voucher_types([x.voucher_type for x in self.vouchers])
def get_existing_ledger_entries(self):
vouchers = [x.voucher_no for x in self.vouchers]
@@ -121,7 +108,7 @@ class RepostAccountingLedger(Document):
return rendered_page
def on_submit(self):
if len(self.vouchers) > 1:
if len(self.vouchers) > 5:
job_name = "repost_accounting_ledger_" + self.name
frappe.enqueue(
method="erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger.start_repost",
@@ -165,7 +152,14 @@ def start_repost(account_repost_doc=str) -> None:
doc.make_gl_entries(1)
doc.make_gl_entries()
frappe.db.commit()
def get_allowed_types_from_settings():
return [
x.document_type
for x in frappe.db.get_all(
"Repost Allowed Types", filters={"allowed": True}, fields=["distinct(document_type)"]
)
]
def validate_docs_for_deferred_accounting(sales_docs, purchase_docs):
@@ -191,6 +185,25 @@ def validate_docs_for_deferred_accounting(sales_docs, purchase_docs):
)
def validate_docs_for_voucher_types(doc_voucher_types):
allowed_types = get_allowed_types_from_settings()
# Validate voucher types
voucher_types = set(doc_voucher_types)
if disallowed_types := voucher_types.difference(allowed_types):
message = "are" if len(disallowed_types) > 1 else "is"
frappe.throw(
_("{0} {1} not allowed to be reposted. Modify {2} to enable reposting.").format(
frappe.bold(comma_and(list(disallowed_types))),
message,
frappe.bold(
frappe.utils.get_link_to_form(
"Repost Accounting Ledger Settings", "Repost Accounting Ledger Settings"
)
),
)
)
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_repost_allowed_types(doctype, txt, searchfield, start, page_len, filters):

View File

@@ -20,18 +20,11 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase):
self.create_company()
self.create_customer()
self.create_item()
self.update_repost_settings()
update_repost_settings()
def teadDown(self):
def tearDown(self):
frappe.db.rollback()
def update_repost_settings(self):
allowed_types = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]
repost_settings = frappe.get_doc("Repost Accounting Ledger Settings")
for x in allowed_types:
repost_settings.append("allowed_types", {"document_type": x, "allowed": True})
repost_settings.save()
def test_01_basic_functions(self):
si = create_sales_invoice(
item=self.item,
@@ -90,9 +83,6 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase):
# Submit repost document
ral.save().submit()
# background jobs don't run on test cases. Manually triggering repost function.
start_repost(ral.name)
res = (
qb.from_(gl)
.select(gl.voucher_no, Sum(gl.debit).as_("debit"), Sum(gl.credit).as_("credit"))
@@ -177,26 +167,6 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase):
pe = get_payment_entry(si.doctype, si.name)
pe.save().submit()
# without deletion flag set
ral = frappe.new_doc("Repost Accounting Ledger")
ral.company = self.company
ral.delete_cancelled_entries = False
ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
ral.append("vouchers", {"voucher_type": pe.doctype, "voucher_no": pe.name})
ral.save()
# assert preview data is generated
preview = ral.generate_preview()
self.assertIsNotNone(preview)
ral.save().submit()
# background jobs don't run on test cases. Manually triggering repost function.
start_repost(ral.name)
self.assertIsNotNone(frappe.db.exists("GL Entry", {"voucher_no": si.name, "is_cancelled": 1}))
self.assertIsNotNone(frappe.db.exists("GL Entry", {"voucher_no": pe.name, "is_cancelled": 1}))
# with deletion flag set
ral = frappe.new_doc("Repost Accounting Ledger")
ral.company = self.company
@@ -205,6 +175,38 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase):
ral.append("vouchers", {"voucher_type": pe.doctype, "voucher_no": pe.name})
ral.save().submit()
start_repost(ral.name)
self.assertIsNone(frappe.db.exists("GL Entry", {"voucher_no": si.name, "is_cancelled": 1}))
self.assertIsNone(frappe.db.exists("GL Entry", {"voucher_no": pe.name, "is_cancelled": 1}))
def test_05_without_deletion_flag(self):
si = create_sales_invoice(
item=self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
rate=100,
)
pe = get_payment_entry(si.doctype, si.name)
pe.save().submit()
# without deletion flag set
ral = frappe.new_doc("Repost Accounting Ledger")
ral.company = self.company
ral.delete_cancelled_entries = False
ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
ral.append("vouchers", {"voucher_type": pe.doctype, "voucher_no": pe.name})
ral.save().submit()
self.assertIsNotNone(frappe.db.exists("GL Entry", {"voucher_no": si.name, "is_cancelled": 1}))
self.assertIsNotNone(frappe.db.exists("GL Entry", {"voucher_no": pe.name, "is_cancelled": 1}))
def update_repost_settings():
allowed_types = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]
repost_settings = frappe.get_doc("Repost Accounting Ledger Settings")
for x in allowed_types:
repost_settings.append("allowed_types", {"document_type": x, "allowed": True})
repost_settings.save()

View File

@@ -43,7 +43,7 @@ def start_payment_ledger_repost(docname=None):
except Exception as e:
frappe.db.rollback()
traceback = frappe.get_traceback()
traceback = frappe.get_traceback(with_context=True)
if traceback:
message = "Traceback: <br>" + traceback
frappe.db.set_value(repost_doc.doctype, repost_doc.name, "repost_error_log", message)

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', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payments", "Unreconcile Payment Entries"];
'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"];
if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
// show debit_to in print format
@@ -91,8 +91,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
if(doc.update_stock) this.show_stock_ledger();
if (doc.docstatus == 1 && doc.outstanding_amount!=0
&& !(cint(doc.is_return) && doc.return_against)) {
if (doc.docstatus == 1 && doc.outstanding_amount!=0) {
this.frm.add_custom_button(
__('Payment'),
() => this.make_payment_entry(),
@@ -178,7 +177,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
}
}
erpnext.accounts.unreconcile_payments.add_unreconcile_btn(me.frm);
erpnext.accounts.unreconcile_payment.add_unreconcile_btn(me.frm);
}
make_maintenance_schedule() {
@@ -890,8 +889,8 @@ frappe.ui.form.on('Sales Invoice', {
frm.events.append_time_log(frm, timesheet, 1.0);
}
});
frm.refresh_field("timesheets");
frm.trigger("calculate_timesheet_totals");
frm.refresh();
},
async get_exchange_rate(frm, from_currency, to_currency) {

View File

@@ -138,6 +138,7 @@
"loyalty_amount",
"column_break_77",
"loyalty_program",
"dont_create_loyalty_points",
"loyalty_redemption_account",
"loyalty_redemption_cost_center",
"contact_and_address_tab",
@@ -1039,8 +1040,7 @@
"label": "Loyalty Program",
"no_copy": 1,
"options": "Loyalty Program",
"print_hide": 1,
"read_only": 1
"print_hide": 1
},
{
"allow_on_submit": 1,
@@ -1613,7 +1613,8 @@
"hide_seconds": 1,
"label": "Inter Company Invoice Reference",
"options": "Purchase Invoice",
"read_only": 1
"read_only": 1,
"search_index": 1
},
{
"fieldname": "customer_group",
@@ -2147,11 +2148,19 @@
"label": "Use Company default Cost Center for Round off"
},
{
"default": "0",
"default": "1",
"depends_on": "eval: doc.is_return",
"fieldname": "update_billed_amount_in_delivery_note",
"fieldtype": "Check",
"label": "Update Billed Amount in Delivery Note"
},
{
"default": "0",
"depends_on": "loyalty_program",
"fieldname": "dont_create_loyalty_points",
"fieldtype": "Check",
"label": "Don't Create Loyalty Points",
"no_copy": 1
}
],
"icon": "fa fa-file-text",
@@ -2164,7 +2173,7 @@
"link_fieldname": "consolidated_invoice"
}
],
"modified": "2023-11-03 14:39:38.012346",
"modified": "2024-01-02 17:25:46.027523",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -17,6 +17,7 @@ from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
)
from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
validate_docs_for_deferred_accounting,
validate_docs_for_voucher_types,
)
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
get_party_tax_withholding_details,
@@ -180,6 +181,7 @@ class SalesInvoice(SellingController):
self.validate_write_off_account()
self.validate_account_for_change_amount()
self.validate_income_account()
validate_docs_for_voucher_types(["Sales Invoice"])
validate_docs_for_deferred_accounting([self.name], [])
def validate_fixed_asset(self):
@@ -243,7 +245,8 @@ class SalesInvoice(SellingController):
self.calculate_taxes_and_totals()
def before_save(self):
set_account_for_mode_of_payment(self)
self.set_account_for_mode_of_payment()
self.set_paid_amount()
def on_submit(self):
self.validate_pos_paid_amount()
@@ -298,7 +301,12 @@ class SalesInvoice(SellingController):
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
# create the loyalty point ledger entry if the customer is enrolled in any loyalty program
if not self.is_return and not self.is_consolidated and self.loyalty_program:
if (
not self.is_return
and not self.is_consolidated
and self.loyalty_program
and not self.dont_create_loyalty_points
):
self.make_loyalty_point_entry()
elif (
self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program
@@ -408,7 +416,7 @@ class SalesInvoice(SellingController):
"Repost Payment Ledger Items",
"Repost Accounting Ledger",
"Repost Accounting Ledger Items",
"Unreconcile Payments",
"Unreconcile Payment",
"Unreconcile Payment Entries",
"Payment Ledger Entry",
)
@@ -531,9 +539,6 @@ class SalesInvoice(SellingController):
):
data.sales_invoice = sales_invoice
def on_update(self):
self.set_paid_amount()
def on_update_after_submit(self):
if hasattr(self, "repost_required"):
fields_to_check = [
@@ -564,6 +569,11 @@ class SalesInvoice(SellingController):
self.paid_amount = paid_amount
self.base_paid_amount = base_paid_amount
def set_account_for_mode_of_payment(self):
for payment in self.payments:
if not payment.account:
payment.account = get_bank_cash_account(payment.mode_of_payment, self.company).get("account")
def validate_time_sheets_are_submitted(self):
for data in self.timesheets:
if data.time_sheet:
@@ -1062,9 +1072,7 @@ class SalesInvoice(SellingController):
"debit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
else grand_total,
"against_voucher": self.return_against
if cint(self.is_return) and self.return_against
else self.name,
"against_voucher": self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center,
"project": self.project,
@@ -1691,12 +1699,8 @@ class SalesInvoice(SellingController):
elif outstanding_amount > 0 and getdate(self.due_date) >= getdate():
self.status = "Unpaid"
# Check if outstanding amount is 0 due to credit note issued against invoice
elif (
outstanding_amount <= 0
and self.is_return == 0
and frappe.db.get_value(
"Sales Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1}
)
elif self.is_return == 0 and frappe.db.get_value(
"Sales Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1}
):
self.status = "Credit Note Issued"
elif self.is_return == 1:
@@ -1932,7 +1936,6 @@ def make_delivery_note(source_name, target_doc=None):
set_missing_values,
)
doclist.set_onload("ignore_price_list", True)
return doclist
@@ -1943,12 +1946,6 @@ def make_sales_return(source_name, target_doc=None):
return make_return_doc("Sales Invoice", source_name, target_doc)
def set_account_for_mode_of_payment(self):
for data in self.payments:
if not data.account:
data.account = get_bank_cash_account(data.mode_of_payment, self.company).get("account")
def get_inter_company_details(doc, doctype):
if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"]:
parties = frappe.db.get_all(
@@ -2194,9 +2191,18 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
def get_received_items(reference_name, doctype, reference_fieldname):
reference_field = "inter_company_invoice_reference"
if doctype == "Purchase Order":
reference_field = "inter_company_order_reference"
filters = {
reference_field: reference_name,
"docstatus": 1,
}
target_doctypes = frappe.get_all(
doctype,
filters={"inter_company_invoice_reference": reference_name, "docstatus": 1},
filters=filters,
as_list=True,
)
@@ -2378,10 +2384,6 @@ def get_loyalty_programs(customer):
return lp_details
def on_doctype_update():
frappe.db.add_index("Sales Invoice", ["customer", "is_return", "return_against"])
@frappe.whitelist()
def create_invoice_discounting(source_name, target_doc=None):
invoice = frappe.get_doc("Sales Invoice", source_name)

View File

@@ -782,6 +782,28 @@ class TestSalesInvoice(FrappeTestCase):
w = self.make()
self.assertEqual(w.outstanding_amount, w.base_rounded_total)
def test_rounded_total_with_cash_discount(self):
si = frappe.copy_doc(test_records[2])
item = copy.deepcopy(si.get("items")[0])
item.update(
{
"qty": 1,
"rate": 14960.66,
}
)
si.set("items", [item])
si.set("taxes", [])
si.apply_discount_on = "Grand Total"
si.is_cash_or_non_trade_discount = 1
si.discount_amount = 1
si.insert()
self.assertEqual(si.grand_total, 14959.66)
self.assertEqual(si.rounded_total, 14960)
self.assertEqual(si.rounding_adjustment, 0.34)
def test_payment(self):
w = self.make()
@@ -1506,8 +1528,21 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(party_credited, 1000)
# Check outstanding amount
self.assertFalse(si1.outstanding_amount)
self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 1500)
self.assertEqual(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"), -1000)
self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 2500)
def test_return_invoice_with_account_mismatch(self):
debtors2 = create_account(
parent_account="Accounts Receivable - _TC",
account_name="Debtors 2",
company="_Test Company",
account_type="Receivable",
)
si = create_sales_invoice(qty=1, rate=1000)
cr_note = create_sales_invoice(
qty=-1, rate=1000, is_return=1, return_against=si.name, debit_to=debtors2, do_not_save=True
)
self.assertRaises(frappe.ValidationError, cr_note.save)
def test_gle_made_when_asset_is_returned(self):
create_asset_data()
@@ -2769,6 +2804,12 @@ class TestSalesInvoice(FrappeTestCase):
@change_settings("Selling Settings", {"enable_discount_accounting": 1})
def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled(self):
from erpnext.accounts.doctype.repost_accounting_ledger.test_repost_accounting_ledger import (
update_repost_settings,
)
update_repost_settings()
additional_discount_account = create_account(
account_name="Discount Account",
parent_account="Indirect Expenses - _TC",

View File

@@ -108,7 +108,7 @@
"fieldname": "rate",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Rate",
"label": "Tax Rate",
"oldfieldname": "rate",
"oldfieldtype": "Currency"
},
@@ -218,7 +218,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2022-10-17 13:08:17.776528",
"modified": "2024-01-14 10:08:17.776528",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Taxes and Charges",
@@ -227,4 +227,4 @@
"sort_field": "modified",
"sort_order": "ASC",
"states": []
}
}

View File

@@ -10,7 +10,7 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
class TestUnreconcilePayments(AccountsTestMixin, FrappeTestCase):
class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
def setUp(self):
self.create_company()
self.create_customer()
@@ -73,7 +73,7 @@ class TestUnreconcilePayments(AccountsTestMixin, FrappeTestCase):
unreconcile = frappe.get_doc(
{
"doctype": "Unreconcile Payments",
"doctype": "Unreconcile Payment",
"company": self.company,
"voucher_type": pe.doctype,
"voucher_no": pe.name,
@@ -138,7 +138,7 @@ class TestUnreconcilePayments(AccountsTestMixin, FrappeTestCase):
unreconcile = frappe.get_doc(
{
"doctype": "Unreconcile Payments",
"doctype": "Unreconcile Payment",
"company": self.company,
"voucher_type": pe2.doctype,
"voucher_no": pe2.name,
@@ -196,7 +196,7 @@ class TestUnreconcilePayments(AccountsTestMixin, FrappeTestCase):
unreconcile = frappe.get_doc(
{
"doctype": "Unreconcile Payments",
"doctype": "Unreconcile Payment",
"company": self.company,
"voucher_type": pe.doctype,
"voucher_no": pe.name,
@@ -281,7 +281,7 @@ class TestUnreconcilePayments(AccountsTestMixin, FrappeTestCase):
unreconcile = frappe.get_doc(
{
"doctype": "Unreconcile Payments",
"doctype": "Unreconcile Payment",
"company": self.company,
"voucher_type": pe2.doctype,
"voucher_no": pe2.name,

View File

@@ -1,7 +1,7 @@
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on("Unreconcile Payments", {
frappe.ui.form.on("Unreconcile Payment", {
refresh(frm) {
frm.set_query("voucher_type", function() {
return {

View File

@@ -21,7 +21,7 @@
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Unreconcile Payments",
"options": "Unreconcile Payment",
"print_hide": 1,
"read_only": 1
},
@@ -61,7 +61,7 @@
"modified": "2023-08-28 17:42:50.261377",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Unreconcile Payments",
"name": "Unreconcile Payment",
"naming_rule": "Expression",
"owner": "Administrator",
"permissions": [
@@ -90,4 +90,4 @@
"sort_order": "DESC",
"states": [],
"track_changes": 1
}
}

View File

@@ -15,7 +15,7 @@ from erpnext.accounts.utils import (
)
class UnreconcilePayments(Document):
class UnreconcilePayment(Document):
def validate(self):
self.supported_types = ["Payment Entry", "Journal Entry"]
if not self.voucher_type in self.supported_types:
@@ -142,7 +142,7 @@ def create_unreconcile_doc_for_selection(selections=None):
selections = frappe.json.loads(selections)
# assuming each row is a unique voucher
for row in selections:
unrecon = frappe.new_doc("Unreconcile Payments")
unrecon = frappe.new_doc("Unreconcile Payment")
unrecon.company = row.get("company")
unrecon.voucher_type = row.get("voucher_type")
unrecon.voucher_no = row.get("voucher_no")

View File

@@ -556,7 +556,12 @@ def get_round_off_account_and_cost_center(
def make_reverse_gl_entries(
gl_entries=None, voucher_type=None, voucher_no=None, adv_adj=False, update_outstanding="Yes"
gl_entries=None,
voucher_type=None,
voucher_no=None,
adv_adj=False,
update_outstanding="Yes",
partial_cancel=False,
):
"""
Get original gl entries of the voucher
@@ -576,14 +581,19 @@ def make_reverse_gl_entries(
if gl_entries:
create_payment_ledger_entry(
gl_entries, cancel=1, adv_adj=adv_adj, update_outstanding=update_outstanding
gl_entries,
cancel=1,
adv_adj=adv_adj,
update_outstanding=update_outstanding,
partial_cancel=partial_cancel,
)
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"])
if not partial_cancel:
set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
for entry in gl_entries:
new_gle = copy.deepcopy(entry)

View File

@@ -31,7 +31,12 @@ from erpnext.accounts.utils import get_fiscal_year
from erpnext.exceptions import InvalidAccountCurrency, PartyDisabled, PartyFrozen
from erpnext.utilities.regional import temporary_flag
PURCHASE_TRANSACTION_TYPES = {"Purchase Order", "Purchase Receipt", "Purchase Invoice"}
PURCHASE_TRANSACTION_TYPES = {
"Supplier Quotation",
"Purchase Order",
"Purchase Receipt",
"Purchase Invoice",
}
SALES_TRANSACTION_TYPES = {
"Quotation",
"Sales Order",
@@ -109,14 +114,12 @@ def _get_party_details(
set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype)
)
party = party_details[party_type.lower()]
if not ignore_permissions and not (
frappe.has_permission(party_type, "read", party)
or frappe.has_permission(party_type, "select", party)
):
frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError)
party = frappe.get_doc(party_type, party)
if not ignore_permissions:
ptype = "select" if frappe.only_has_select_perm(party_type) else "read"
frappe.has_permission(party_type, ptype, party, throw=True)
currency = party.get("default_currency") or currency or get_company_currency(company)
party_address, shipping_address = set_address_details(
@@ -190,7 +193,7 @@ def set_address_details(
company_address=None,
shipping_address=None,
*,
ignore_permissions=False
ignore_permissions=False,
):
billing_address_field = (
"customer_address" if party_type == "Lead" else party_type.lower() + "_address"
@@ -231,8 +234,10 @@ def set_address_details(
if shipping_address:
party_details.update(
shipping_address=shipping_address,
shipping_address_display=render_address(shipping_address),
**get_fetch_values(doctype, "shipping_address", shipping_address)
shipping_address_display=render_address(
shipping_address, check_permissions=not ignore_permissions
),
**get_fetch_values(doctype, "shipping_address", shipping_address),
)
if party_details.company_address:
@@ -243,7 +248,7 @@ def set_address_details(
party_details.company_address_display
or render_address(party_details.company_address, check_permissions=False)
),
**get_fetch_values(doctype, "billing_address", party_details.company_address)
**get_fetch_values(doctype, "billing_address", party_details.company_address),
)
# shipping address - if not already set
@@ -251,7 +256,7 @@ def set_address_details(
party_details.update(
shipping_address=party_details.billing_address,
shipping_address_display=party_details.billing_address_display,
**get_fetch_values(doctype, "shipping_address", party_details.billing_address)
**get_fetch_values(doctype, "shipping_address", party_details.billing_address),
)
party_address, shipping_address = (
@@ -949,6 +954,9 @@ def get_partywise_advanced_payment_amount(
if party:
query = query.where(ple.party == party)
if invoice_doctypes := frappe.get_hooks("invoice_doctypes"):
query = query.where(ple.voucher_type.notin(invoice_doctypes))
data = query.run()
if data:
return frappe._dict(data)

View File

@@ -149,11 +149,16 @@ frappe.query_reports["Accounts Payable"] = {
"label": __("Revaluation Journals"),
"fieldtype": "Check",
},
{
"fieldname": "in_party_currency",
"label": __("In Party Currency"),
"fieldtype": "Check",
},
{
"fieldname": "ignore_accounts",
"label": __("Group by Voucher"),
"fieldtype": "Check",
}
},
],

View File

@@ -40,6 +40,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
"range2": 60,
"range3": 90,
"range4": 120,
"in_party_currency": 1,
}
data = execute(filters)

View File

@@ -10,10 +10,8 @@
<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
<h4 class="text-center">
{% if (filters.customer_name) { %}
{%= filters.customer_name %}
{% } else { %}
{%= filters.customer || filters.supplier %}
{% if (filters.party) { %}
{%= __(filters.party) %}
{% } %}
</h4>
<h6 class="text-center">
@@ -141,7 +139,7 @@
<th style="width: 24%">{%= __("Reference") %}</th>
{% } %}
{% if(!filters.show_future_payments) { %}
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
<th style="width: 20%">{%= (filters.party) ? __("Remarks"): __("Party") %}</th>
{% } %}
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
{% if(!filters.show_future_payments) { %}
@@ -158,7 +156,7 @@
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
{% } %}
{% } else { %}
<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
<th style="width: 40%">{%= (filters.party) ? __("Remarks"): __("Party") %}</th>
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
@@ -187,7 +185,7 @@
{% if(!filters.show_future_payments) { %}
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{% if(!(filters.party)) { %}
{%= data[i]["party"] %}
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
<br> {%= data[i]["customer_name"] %}
@@ -260,7 +258,7 @@
{% if(data[i]["party"]|| "&nbsp;") { %}
{% if(!data[i]["is_total_row"]) { %}
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{% if(!(filters.party)) { %}
{%= data[i]["party"] %}
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
<br> {%= data[i]["customer_name"] %}

View File

@@ -181,13 +181,17 @@ frappe.query_reports["Accounts Receivable"] = {
"label": __("Revaluation Journals"),
"fieldtype": "Check",
},
{
"fieldname": "in_party_currency",
"label": __("In Party Currency"),
"fieldtype": "Check",
},
{
"fieldname": "ignore_accounts",
"label": __("Group by Voucher"),
"fieldtype": "Check",
}
],
"formatter": function(value, row, column, data, default_formatter) {

View File

@@ -5,9 +5,9 @@
from collections import OrderedDict
import frappe
from frappe import _, qb, scrub
from frappe import _, qb, query_builder, scrub
from frappe.query_builder import Criterion
from frappe.query_builder.functions import Date, Sum
from frappe.query_builder.functions import Date, Substring, Sum
from frappe.utils import cint, cstr, flt, getdate, nowdate
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@@ -28,8 +28,8 @@ from erpnext.accounts.utils import get_currency_precision
# 6. Configurable Ageing Groups (0-30, 30-60 etc) can be set via filters
# 7. For overpayment against an invoice with payment terms, there will be an additional row
# 8. Invoice details like Sales Persons, Delivery Notes are also fetched comma separated
# 9. Report amounts are in "Party Currency" if party is selected, or company currency for multi-party
# 10. This reports is based on all GL Entries that are made against account_type "Receivable" or "Payable"
# 9. Report amounts are in party currency if in_party_currency is selected, otherwise company currency
# 10. This report is based on Payment Ledger Entries
def execute(filters=None):
@@ -84,6 +84,9 @@ class ReceivablePayableReport(object):
self.total_row_map = {}
self.skip_total_row = 1
if self.filters.get("in_party_currency"):
self.skip_total_row = 1
def get_data(self):
self.get_ple_entries()
self.get_sales_invoices_or_customers_based_on_sales_person()
@@ -145,7 +148,7 @@ class ReceivablePayableReport(object):
if self.filters.get("group_by_party"):
self.init_subtotal_row(ple.party)
if self.filters.get("group_by_party"):
if self.filters.get("group_by_party") and not self.filters.get("in_party_currency"):
self.init_subtotal_row("Total")
def get_invoices(self, ple):
@@ -224,8 +227,7 @@ class ReceivablePayableReport(object):
if not row:
return
# amount in "Party Currency", if its supplied. If not, amount in company currency
if self.filters.get("party_type") and self.filters.get("party"):
if self.filters.get("in_party_currency") or self.filters.get("party_account"):
amount = ple.amount_in_account_currency
else:
amount = ple.amount
@@ -244,8 +246,12 @@ class ReceivablePayableReport(object):
row.invoiced_in_account_currency += amount_in_account_currency
else:
if self.is_invoice(ple):
row.credit_note -= amount
row.credit_note_in_account_currency -= amount_in_account_currency
if row.voucher_no == ple.voucher_no == ple.against_voucher_no:
row.paid -= amount
row.paid_in_account_currency -= amount_in_account_currency
else:
row.credit_note -= amount
row.credit_note_in_account_currency -= amount_in_account_currency
else:
row.paid -= amount
row.paid_in_account_currency -= amount_in_account_currency
@@ -256,8 +262,10 @@ class ReceivablePayableReport(object):
def update_sub_total_row(self, row, party):
total_row = self.total_row_map.get(party)
for field in self.get_currency_fields():
total_row[field] += row.get(field, 0.0)
if total_row:
for field in self.get_currency_fields():
total_row[field] += row.get(field, 0.0)
total_row["currency"] = row.get("currency", "")
def append_subtotal_row(self, party):
sub_total_row = self.total_row_map.get(party)
@@ -283,8 +291,8 @@ class ReceivablePayableReport(object):
must_consider = False
if self.filters.get("for_revaluation_journals"):
if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) or (
(abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision)
if (abs(row.outstanding) > 0.0 / 10**self.currency_precision) or (
(abs(row.outstanding_in_account_currency) > 0.0 / 10**self.currency_precision)
):
must_consider = True
else:
@@ -318,7 +326,7 @@ class ReceivablePayableReport(object):
if self.filters.get("group_by_party"):
self.append_subtotal_row(self.previous_party)
if self.data:
self.data.append(self.total_row_map.get("Total"))
self.data.append(self.total_row_map.get("Total", {}))
def append_row(self, row):
self.allocate_future_payments(row)
@@ -449,7 +457,7 @@ class ReceivablePayableReport(object):
party_details = self.get_party_details(row.party) or {}
row.update(party_details)
if self.filters.get("party_type") and self.filters.get("party"):
if self.filters.get("in_party_currency") or self.filters.get("party_account"):
row.currency = row.account_currency
else:
row.currency = self.company_currency
@@ -570,6 +578,8 @@ class ReceivablePayableReport(object):
def get_future_payments_from_payment_entry(self):
pe = frappe.qb.DocType("Payment Entry")
pe_ref = frappe.qb.DocType("Payment Entry Reference")
ifelse = query_builder.CustomFunction("IF", ["condition", "then", "else"])
return (
frappe.qb.from_(pe)
.inner_join(pe_ref)
@@ -581,6 +591,11 @@ class ReceivablePayableReport(object):
(pe.posting_date).as_("future_date"),
(pe_ref.allocated_amount).as_("future_amount"),
(pe.reference_no).as_("future_ref"),
ifelse(
pe.payment_type == "Receive",
pe.source_exchange_rate * pe_ref.allocated_amount,
pe.target_exchange_rate * pe_ref.allocated_amount,
).as_("future_amount_in_base_currency"),
)
.where(
(pe.docstatus < 2)
@@ -617,13 +632,24 @@ class ReceivablePayableReport(object):
query = query.select(
Sum(jea.debit_in_account_currency - jea.credit_in_account_currency).as_("future_amount")
)
query = query.select(Sum(jea.debit - jea.credit).as_("future_amount_in_base_currency"))
else:
query = query.select(
Sum(jea.credit_in_account_currency - jea.debit_in_account_currency).as_("future_amount")
)
query = query.select(Sum(jea.credit - jea.debit).as_("future_amount_in_base_currency"))
else:
query = query.select(
Sum(jea.debit if self.account_type == "Payable" else jea.credit).as_("future_amount")
Sum(jea.debit if self.account_type == "Payable" else jea.credit).as_(
"future_amount_in_base_currency"
)
)
query = query.select(
Sum(
jea.debit_in_account_currency
if self.account_type == "Payable"
else jea.credit_in_account_currency
).as_("future_amount")
)
query = query.having(qb.Field("future_amount") > 0)
@@ -639,14 +665,19 @@ class ReceivablePayableReport(object):
row.remaining_balance = row.outstanding
row.future_amount = 0.0
for future in self.future_payments.get((row.voucher_no, row.party), []):
if row.remaining_balance > 0 and future.future_amount:
if future.future_amount > row.outstanding:
if self.filters.in_party_currency:
future_amount_field = "future_amount"
else:
future_amount_field = "future_amount_in_base_currency"
if row.remaining_balance > 0 and future.get(future_amount_field):
if future.get(future_amount_field) > row.outstanding:
row.future_amount = row.outstanding
future.future_amount = future.future_amount - row.outstanding
future[future_amount_field] = future.get(future_amount_field) - row.outstanding
row.remaining_balance = 0
else:
row.future_amount += future.future_amount
future.future_amount = 0
row.future_amount += future.get(future_amount_field)
future[future_amount_field] = 0
row.remaining_balance = row.outstanding - row.future_amount
row.setdefault("future_ref", []).append(
@@ -762,7 +793,12 @@ class ReceivablePayableReport(object):
)
if self.filters.get("show_remarks"):
query = query.select(ple.remarks)
if remarks_length := frappe.db.get_single_value(
"Accounts Settings", "receivable_payable_remarks_length"
):
query = query.select(Substring(ple.remarks, 1, remarks_length).as_("remarks"))
else:
query = query.select(ple.remarks)
if self.filters.get("group_by_party"):
query = query.orderby(self.ple.party, self.ple.posting_date)
@@ -1080,7 +1116,7 @@ class ReceivablePayableReport(object):
)
if self.filters.show_remarks:
self.add_column(label=_("Remarks"), fieldname="remarks", fieldtype="Text", width=200),
self.add_column(label=_("Remarks"), fieldname="remarks", fieldtype="Text", width=200)
def add_column(self, label, fieldname=None, fieldtype="Currency", options=None, width=120):
if not fieldname:

View File

@@ -76,6 +76,41 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
return credit_note
def test_pos_receivable(self):
filters = {
"company": self.company,
"party_type": "Customer",
"party": [self.customer],
"report_date": add_days(today(), 2),
"based_on_payment_terms": 0,
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
"show_remarks": False,
}
pos_inv = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
pos_inv.posting_date = add_days(today(), 2)
pos_inv.is_pos = 1
pos_inv.append(
"payments",
frappe._dict(
mode_of_payment="Cash",
amount=flt(pos_inv.grand_total / 2),
),
)
pos_inv.disable_rounded_total = 1
pos_inv.save()
pos_inv.submit()
report = execute(filters)
expected_data = [[pos_inv.grand_total, pos_inv.paid_amount, 0]]
row = report[1][-1]
self.assertEqual(expected_data[0], [row.invoiced, row.paid, row.credit_note])
pos_inv.cancel()
def test_accounts_receivable(self):
filters = {
"company": self.company,
@@ -544,7 +579,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
filters.update({"party_account": self.debtors_usd})
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [8000.0, 8000.0, self.debtors_usd, si2.currency]
expected_data = [100.0, 100.0, self.debtors_usd, si2.currency]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.party_account, row.account_currency]
@@ -581,6 +616,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
"range2": 60,
"range3": 90,
"range4": 120,
"in_party_currency": 1,
}
si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
@@ -736,3 +772,92 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
# post sorting output should be [[Additional Debtors, ...], [Debtors, ...]]
report_output = sorted(report_output, key=lambda x: x[0])
self.assertEqual(expected_data, report_output)
def test_future_payments_on_foreign_currency(self):
self.customer2 = (
frappe.get_doc(
{
"doctype": "Customer",
"customer_name": "Jane Doe",
"type": "Individual",
"default_currency": "USD",
}
)
.insert()
.submit()
)
si = self.create_sales_invoice(do_not_submit=True)
si.posting_date = add_days(today(), -1)
si.customer = self.customer2
si.currency = "USD"
si.conversion_rate = 80
si.debit_to = self.debtors_usd
si.save().submit()
# full payment in USD
pe = get_payment_entry(si.doctype, si.name)
pe.posting_date = add_days(today(), 1)
pe.base_received_amount = 7500
pe.received_amount = 7500
pe.source_exchange_rate = 75
pe.save().submit()
filters = frappe._dict(
{
"company": self.company,
"report_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
"show_future_payments": True,
"in_party_currency": False,
}
)
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [8000.0, 8000.0, 500.0, 7500.0]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount]
)
filters.in_party_currency = True
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [100.0, 100.0, 0.0, 100.0]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount]
)
pe.cancel()
# partial payment in USD on a future date
pe = get_payment_entry(si.doctype, si.name)
pe.posting_date = add_days(today(), 1)
pe.base_received_amount = 6750
pe.received_amount = 6750
pe.source_exchange_rate = 75
pe.paid_amount = 90 # in USD
pe.references[0].allocated_amount = 90
pe.save().submit()
filters.in_party_currency = False
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [8000.0, 8000.0, 1250.0, 6750.0]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount]
)
filters.in_party_currency = True
report = execute(filters)[1]
self.assertEqual(len(report), 1)
expected_data = [100.0, 100.0, 10.0, 90.0]
row = report[0]
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount]
)

View File

@@ -8,6 +8,7 @@ from frappe.utils import cint, flt
from erpnext.accounts.party import get_partywise_advanced_payment_amount
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
from erpnext.accounts.utils import get_currency_precision
def execute(filters=None):
@@ -35,6 +36,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
def get_data(self, args):
self.data = []
self.receivables = ReceivablePayableReport(self.filters).run(args)[1]
self.currency_precision = get_currency_precision() or 2
self.get_party_total(args)
@@ -58,7 +60,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
gl_balance_map = get_gl_balance(self.filters.report_date, self.filters.company)
for party, party_dict in self.party_total.items():
if party_dict.outstanding == 0:
if flt(party_dict.outstanding, self.currency_precision) == 0:
continue
row = frappe._dict()

View File

@@ -2,7 +2,44 @@
// License: GNU General Public License v3. See license.txt
frappe.query_reports["Budget Variance Report"] = {
"filters": [
"filters": get_filters(),
"formatter": function (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
if (column.fieldname.includes(__("variance"))) {
if (data[column.fieldname] < 0) {
value = "<span style='color:red'>" + value + "</span>";
}
else if (data[column.fieldname] > 0) {
value = "<span style='color:green'>" + value + "</span>";
}
}
return value;
}
}
function get_filters() {
function get_dimensions() {
let result = [];
frappe.call({
method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimensions",
args: {
'with_cost_center_and_project': true
},
async: false,
callback: function(r) {
if(!r.exc) {
result = r.message[0].map(elem => elem.document_type);
}
}
});
return result;
}
let budget_against_options = get_dimensions();
let filters = [
{
fieldname: "from_fiscal_year",
label: __("From Fiscal Year"),
@@ -44,7 +81,7 @@ frappe.query_reports["Budget Variance Report"] = {
fieldname: "budget_against",
label: __("Budget Against"),
fieldtype: "Select",
options: ["Cost Center", "Project"],
options: budget_against_options,
default: "Cost Center",
reqd: 1,
on_change: function() {
@@ -71,24 +108,8 @@ frappe.query_reports["Budget Variance Report"] = {
fieldtype: "Check",
default: 0,
},
],
"formatter": function (value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
]
if (column.fieldname.includes(__("variance"))) {
if (data[column.fieldname] < 0) {
value = "<span style='color:red'>" + value + "</span>";
}
else if (data[column.fieldname] > 0) {
value = "<span style='color:green'>" + value + "</span>";
}
}
return value;
}
return filters;
}
erpnext.dimension_filters.forEach((dimension) => {
frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]);
});

View File

@@ -129,7 +129,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
}
value = default_formatter(value, row, column, data);
if (!data.parent_account) {
if (data && !data.parent_account) {
value = $(`<span>${value}</span>`);
var $value = $(value).css("font-weight", "bold");

View File

@@ -3,7 +3,7 @@
import frappe
from frappe import _, scrub
from frappe import _, qb, scrub
from frappe.utils import getdate, nowdate
@@ -38,7 +38,6 @@ class PartyLedgerSummaryReport(object):
"""
Additional Columns for 'User Permission' based access control
"""
from frappe import qb
if self.filters.party_type == "Customer":
self.territories = frappe._dict({})
@@ -365,13 +364,33 @@ class PartyLedgerSummaryReport(object):
def get_party_adjustment_amounts(self):
conditions = self.prepare_conditions()
income_or_expense = (
"Expense Account" if self.filters.party_type == "Customer" else "Income Account"
account_type = "Expense Account" if self.filters.party_type == "Customer" else "Income Account"
income_or_expense_accounts = frappe.db.get_all(
"Account", filters={"account_type": account_type, "company": self.filters.company}, pluck="name"
)
invoice_dr_or_cr = "debit" if self.filters.party_type == "Customer" else "credit"
reverse_dr_or_cr = "credit" if self.filters.party_type == "Customer" else "debit"
round_off_account = frappe.get_cached_value("Company", self.filters.company, "round_off_account")
gl = qb.DocType("GL Entry")
if not income_or_expense_accounts:
# prevent empty 'in' condition
income_or_expense_accounts.append("")
else:
# escape '%' in account name
# ignoring frappe.db.escape as it replaces single quotes with double quotes
income_or_expense_accounts = [x.replace("%", "%%") for x in income_or_expense_accounts]
accounts_query = (
qb.from_(gl)
.select(gl.voucher_type, gl.voucher_no)
.where(
(gl.account.isin(income_or_expense_accounts))
& (gl.posting_date.gte(self.filters.from_date))
& (gl.posting_date.lte(self.filters.to_date))
)
)
gl_entries = frappe.db.sql(
"""
select
@@ -381,16 +400,15 @@ class PartyLedgerSummaryReport(object):
where
docstatus < 2 and is_cancelled = 0
and (voucher_type, voucher_no) in (
select voucher_type, voucher_no from `tabGL Entry` gle, `tabAccount` acc
where acc.name = gle.account and acc.account_type = '{income_or_expense}'
and gle.posting_date between %(from_date)s and %(to_date)s and gle.docstatus < 2
{accounts_query}
) and (voucher_type, voucher_no) in (
select voucher_type, voucher_no from `tabGL Entry` gle
where gle.party_type=%(party_type)s and ifnull(party, '') != ''
and gle.posting_date between %(from_date)s and %(to_date)s and gle.docstatus < 2 {conditions}
)
""".format(
conditions=conditions, income_or_expense=income_or_expense
""".format(
accounts_query=accounts_query,
conditions=conditions,
),
self.filters,
as_dict=True,
@@ -414,7 +432,7 @@ class PartyLedgerSummaryReport(object):
elif gle.party:
parties.setdefault(gle.party, 0)
parties[gle.party] += gle.get(reverse_dr_or_cr) - gle.get(invoice_dr_or_cr)
elif frappe.get_cached_value("Account", gle.account, "account_type") == income_or_expense:
elif frappe.get_cached_value("Account", gle.account, "account_type") == account_type:
accounts.setdefault(gle.account, 0)
accounts[gle.account] += gle.get(invoice_dr_or_cr) - gle.get(reverse_dr_or_cr)
else:

View File

@@ -164,7 +164,12 @@ def get_gl_entries(filters, accounting_dimensions):
credit_in_account_currency """
if filters.get("show_remarks"):
select_fields += """,remarks"""
if remarks_length := frappe.db.get_single_value(
"Accounts Settings", "general_ledger_remarks_length"
):
select_fields += f",substr(remarks, 1, {remarks_length}) as 'remarks'"
else:
select_fields += """,remarks"""
order_by_statement = "order by posting_date, account, creation"
@@ -226,6 +231,9 @@ def get_conditions(filters):
if filters.get("voucher_no"):
conditions.append("voucher_no=%(voucher_no)s")
if filters.get("voucher_no_not_in"):
conditions.append("voucher_no not in %(voucher_no_not_in)s")
if filters.get("group_by") == "Group by Party" and not filters.get("party_type"):
conditions.append("party_type in ('Customer', 'Supplier')")
@@ -277,7 +285,8 @@ def get_conditions(filters):
if accounting_dimensions:
for dimension in accounting_dimensions:
if not dimension.disabled:
# Ignore 'Finance Book' set up as dimension in below logic, as it is already handled in above section
if not dimension.disabled and dimension.document_type != "Finance Book":
if filters.get(dimension.fieldname):
if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
filters[dimension.fieldname] = get_dimension_with_children(

View File

@@ -134,7 +134,7 @@ def get_revenue(data, period_list, include_in_gross=1):
def remove_parent_with_no_child(data):
data_to_be_removed = False
for parent in data:
for parent in list(data):
if "is_group" in parent and parent.get("is_group") == 1:
have_child = False
for child in data:

View File

@@ -309,7 +309,8 @@ def get_conditions(filters):
def get_items(filters, additional_query_columns):
conditions = get_conditions(filters)
if additional_query_columns:
additional_query_columns = "," + ",".join(additional_query_columns)
return frappe.db.sql(
"""
select

View File

@@ -381,7 +381,8 @@ def get_group_by_conditions(filters, doctype):
def get_items(filters, additional_query_columns, additional_conditions=None):
conditions = get_conditions(filters, additional_conditions)
if additional_query_columns:
additional_query_columns = "," + ",".join(additional_query_columns)
return frappe.db.sql(
"""
select

View File

@@ -119,8 +119,4 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"initial_depth": 3
}
erpnext.dimension_filters.forEach((dimension) => {
frappe.query_reports["Profitability Analysis"].filters[1].options.push(dimension["document_type"]);
});
});

View File

@@ -52,6 +52,12 @@ frappe.query_reports["Purchase Register"] = {
"label": __("Item Group"),
"fieldtype": "Link",
"options": "Item Group"
},
{
"fieldname": "include_payments",
"label": __("Show Ledger View"),
"fieldtype": "Check",
"default": 0
}
]
}

View File

@@ -4,13 +4,22 @@
import frappe
from frappe import _, msgprint
from frappe.utils import flt
from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt, getdate
from pypika import Order
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
get_dimension_with_children,
from erpnext.accounts.party import get_party_account
from erpnext.accounts.report.utils import (
apply_common_conditions,
get_advance_taxes_and_charges,
get_journal_entries,
get_opening_row,
get_party_details,
get_payment_entries,
get_query_columns,
get_taxes_query,
get_values_for_columns,
)
from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns
def execute(filters=None):
@@ -21,9 +30,15 @@ def _execute(filters=None, additional_table_columns=None):
if not filters:
filters = {}
include_payments = filters.get("include_payments")
if filters.get("include_payments") and not filters.get("supplier"):
frappe.throw(_("Please select a supplier for fetching payments."))
invoice_list = get_invoices(filters, get_query_columns(additional_table_columns))
if filters.get("include_payments"):
invoice_list += get_payments(filters)
columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns(
invoice_list, additional_table_columns
invoice_list, additional_table_columns, include_payments
)
if not invoice_list:
@@ -33,14 +48,28 @@ def _execute(filters=None, additional_table_columns=None):
invoice_expense_map = get_invoice_expense_map(invoice_list)
internal_invoice_map = get_internal_invoice_map(invoice_list)
invoice_expense_map, invoice_tax_map = get_invoice_tax_map(
invoice_list, invoice_expense_map, expense_accounts
invoice_list, invoice_expense_map, expense_accounts, include_payments
)
invoice_po_pr_map = get_invoice_po_pr_map(invoice_list)
suppliers = list(set(d.supplier for d in invoice_list))
supplier_details = get_supplier_details(suppliers)
supplier_details = get_party_details("Supplier", suppliers)
company_currency = frappe.get_cached_value("Company", filters.company, "default_currency")
res = []
if include_payments:
opening_row = get_opening_row(
"Supplier", filters.supplier, getdate(filters.from_date), filters.company
)[0]
res.append(
{
"payable_account": opening_row.account,
"debit": flt(opening_row.debit),
"credit": flt(opening_row.credit),
"balance": flt(opening_row.balance),
}
)
data = []
for inv in invoice_list:
# invoice details
@@ -48,24 +77,25 @@ def _execute(filters=None, additional_table_columns=None):
purchase_receipt = list(set(invoice_po_pr_map.get(inv.name, {}).get("purchase_receipt", [])))
project = list(set(invoice_po_pr_map.get(inv.name, {}).get("project", [])))
row = [
inv.name,
inv.posting_date,
inv.supplier,
inv.supplier_name,
*get_values_for_columns(additional_table_columns, inv).values(),
supplier_details.get(inv.supplier), # supplier_group
inv.tax_id,
inv.credit_to,
inv.mode_of_payment,
", ".join(project),
inv.bill_no,
inv.bill_date,
inv.remarks,
", ".join(purchase_order),
", ".join(purchase_receipt),
company_currency,
]
row = {
"voucher_type": inv.doctype,
"voucher_no": inv.name,
"posting_date": inv.posting_date,
"supplier_id": inv.supplier,
"supplier_name": inv.supplier_name,
**get_values_for_columns(additional_table_columns, inv),
"supplier_group": supplier_details.get(inv.supplier).get("supplier_group"), # supplier_group
"tax_id": supplier_details.get(inv.supplier).get("tax_id"),
"payable_account": inv.credit_to,
"mode_of_payment": inv.mode_of_payment,
"project": ", ".join(project) if inv.doctype == "Purchase Invoice" else inv.project,
"bill_no": inv.bill_no,
"bill_date": inv.bill_date,
"remarks": inv.remarks,
"purchase_order": ", ".join(purchase_order),
"purchase_receipt": ", ".join(purchase_receipt),
"currency": company_currency,
}
# map expense values
base_net_total = 0
@@ -75,14 +105,16 @@ def _execute(filters=None, additional_table_columns=None):
else:
expense_amount = flt(invoice_expense_map.get(inv.name, {}).get(expense_acc))
base_net_total += expense_amount
row.append(expense_amount)
row.update({frappe.scrub(expense_acc): expense_amount})
# Add amount in unrealized account
for account in unrealized_profit_loss_accounts:
row.append(flt(internal_invoice_map.get((inv.name, account))))
row.update(
{frappe.scrub(account + "_unrealized"): flt(internal_invoice_map.get((inv.name, account)))}
)
# net total
row.append(base_net_total or inv.base_net_total)
row.update({"net_total": base_net_total or inv.base_net_total})
# tax account
total_tax = 0
@@ -90,45 +122,190 @@ def _execute(filters=None, additional_table_columns=None):
if tax_acc not in expense_accounts:
tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc))
total_tax += tax_amount
row.append(tax_amount)
row.update({frappe.scrub(tax_acc): tax_amount})
# total tax, grand total, rounded total & outstanding amount
row += [total_tax, inv.base_grand_total, flt(inv.base_grand_total, 0), inv.outstanding_amount]
row.update(
{
"total_tax": total_tax,
"grand_total": inv.base_grand_total,
"rounded_total": inv.base_rounded_total,
"outstanding_amount": inv.outstanding_amount,
}
)
if inv.doctype == "Purchase Invoice":
row.update({"debit": inv.base_grand_total, "credit": 0.0})
else:
row.update({"debit": 0.0, "credit": inv.base_grand_total})
data.append(row)
return columns, data
res += sorted(data, key=lambda x: x["posting_date"])
if include_payments:
running_balance = flt(opening_row.balance)
for row in range(1, len(res)):
running_balance += res[row]["debit"] - res[row]["credit"]
res[row].update({"balance": running_balance})
return columns, res, None, None, None, include_payments
def get_columns(invoice_list, additional_table_columns):
def get_columns(invoice_list, additional_table_columns, include_payments=False):
"""return columns based on filters"""
columns = [
_("Invoice") + ":Link/Purchase Invoice:120",
_("Posting Date") + ":Date:80",
_("Supplier Id") + "::120",
_("Supplier Name") + "::120",
{
"label": _("Voucher Type"),
"fieldname": "voucher_type",
"width": 120,
},
{
"label": _("Voucher"),
"fieldname": "voucher_no",
"fieldtype": "Dynamic Link",
"options": "voucher_type",
"width": 120,
},
{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 80},
{
"label": _("Supplier"),
"fieldname": "supplier_id",
"fieldtype": "Link",
"options": "Supplier",
"width": 120,
},
{"label": _("Supplier Name"), "fieldname": "supplier_name", "fieldtype": "Data", "width": 120},
]
if additional_table_columns:
if additional_table_columns and not include_payments:
columns += additional_table_columns
columns += [
_("Supplier Group") + ":Link/Supplier Group:120",
_("Tax Id") + "::80",
_("Payable Account") + ":Link/Account:120",
_("Mode of Payment") + ":Link/Mode of Payment:80",
_("Project") + ":Link/Project:80",
_("Bill No") + "::120",
_("Bill Date") + ":Date:80",
_("Remarks") + "::150",
_("Purchase Order") + ":Link/Purchase Order:100",
_("Purchase Receipt") + ":Link/Purchase Receipt:100",
{"fieldname": "currency", "label": _("Currency"), "fieldtype": "Data", "width": 80},
]
if not include_payments:
columns += [
{
"label": _("Supplier Group"),
"fieldname": "supplier_group",
"fieldtype": "Link",
"options": "Supplier Group",
"width": 120,
},
{"label": _("Tax Id"), "fieldname": "tax_id", "fieldtype": "Data", "width": 80},
{
"label": _("Payable Account"),
"fieldname": "payable_account",
"fieldtype": "Link",
"options": "Account",
"width": 100,
},
{
"label": _("Mode Of Payment"),
"fieldname": "mode_of_payment",
"fieldtype": "Data",
"width": 120,
},
{
"label": _("Project"),
"fieldname": "project",
"fieldtype": "Link",
"options": "Project",
"width": 80,
},
{"label": _("Bill No"), "fieldname": "bill_no", "fieldtype": "Data", "width": 120},
{"label": _("Bill Date"), "fieldname": "bill_date", "fieldtype": "Date", "width": 80},
{
"label": _("Purchase Order"),
"fieldname": "purchase_order",
"fieldtype": "Link",
"options": "Purchase Order",
"width": 100,
},
{
"label": _("Purchase Receipt"),
"fieldname": "purchase_receipt",
"fieldtype": "Link",
"options": "Purchase Receipt",
"width": 100,
},
{"fieldname": "currency", "label": _("Currency"), "fieldtype": "Data", "width": 80},
]
else:
columns += [
{
"fieldname": "payable_account",
"label": _("Payable Account"),
"fieldtype": "Link",
"options": "Account",
"width": 120,
},
{"fieldname": "debit", "label": _("Debit"), "fieldtype": "Currency", "width": 120},
{"fieldname": "credit", "label": _("Credit"), "fieldtype": "Currency", "width": 120},
{"fieldname": "balance", "label": _("Balance"), "fieldtype": "Currency", "width": 120},
]
account_columns, accounts = get_account_columns(invoice_list, include_payments)
columns = (
columns
+ account_columns[0]
+ account_columns[1]
+ [
{
"label": _("Net Total"),
"fieldname": "net_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
]
+ account_columns[2]
+ [
{
"label": _("Total Tax"),
"fieldname": "total_tax",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
]
)
if not include_payments:
columns += [
{
"label": _("Grand Total"),
"fieldname": "grand_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Rounded Total"),
"fieldname": "rounded_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Outstanding Amount"),
"fieldname": "outstanding_amount",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
]
columns += [{"label": _("Remarks"), "fieldname": "remarks", "fieldtype": "Data", "width": 120}]
return columns, accounts[0], accounts[2], accounts[1]
def get_account_columns(invoice_list, include_payments):
expense_accounts = []
tax_accounts = []
unrealized_profit_loss_accounts = []
expense_columns = []
tax_columns = []
unrealized_profit_loss_account_columns = []
if invoice_list:
expense_accounts = frappe.db.sql_list(
"""select distinct expense_account
@@ -139,15 +316,18 @@ def get_columns(invoice_list, additional_table_columns):
tuple([inv.name for inv in invoice_list]),
)
tax_accounts = frappe.db.sql_list(
"""select distinct account_head
from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice'
and docstatus = 1 and (account_head is not null and account_head != '')
and category in ('Total', 'Valuation and Total')
and parent in (%s) order by account_head"""
% ", ".join(["%s"] * len(invoice_list)),
tuple(inv.name for inv in invoice_list),
purchase_taxes_query = get_taxes_query(
invoice_list, "Purchase Taxes and Charges", "Purchase Invoice"
)
purchase_tax_accounts = purchase_taxes_query.run(as_dict=True, pluck="account_head")
tax_accounts = purchase_tax_accounts
if include_payments:
advance_taxes_query = get_taxes_query(
invoice_list, "Advance Taxes and Charges", "Payment Entry"
)
advance_tax_accounts = advance_taxes_query.run(as_dict=True, pluck="account_head")
tax_accounts = set(tax_accounts + advance_tax_accounts)
unrealized_profit_loss_accounts = frappe.db.sql_list(
"""SELECT distinct unrealized_profit_loss_account
@@ -158,108 +338,109 @@ def get_columns(invoice_list, additional_table_columns):
tuple(inv.name for inv in invoice_list),
)
expense_columns = [(account + ":Currency/currency:120") for account in expense_accounts]
unrealized_profit_loss_account_columns = [
(account + ":Currency/currency:120") for account in unrealized_profit_loss_accounts
]
tax_columns = [
(account + ":Currency/currency:120")
for account in tax_accounts
if account not in expense_accounts
]
for account in expense_accounts:
expense_columns.append(
{
"label": account,
"fieldname": frappe.scrub(account),
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
)
columns = (
columns
+ expense_columns
+ unrealized_profit_loss_account_columns
+ [_("Net Total") + ":Currency/currency:120"]
+ tax_columns
+ [
_("Total Tax") + ":Currency/currency:120",
_("Grand Total") + ":Currency/currency:120",
_("Rounded Total") + ":Currency/currency:120",
_("Outstanding Amount") + ":Currency/currency:120",
]
)
for account in tax_accounts:
if account not in expense_accounts:
tax_columns.append(
{
"label": account,
"fieldname": frappe.scrub(account),
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
)
return columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts
for account in unrealized_profit_loss_accounts:
unrealized_profit_loss_account_columns.append(
{
"label": account,
"fieldname": frappe.scrub(account),
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
)
columns = [expense_columns, unrealized_profit_loss_account_columns, tax_columns]
accounts = [expense_accounts, unrealized_profit_loss_accounts, tax_accounts]
def get_conditions(filters):
conditions = ""
if filters.get("company"):
conditions += " and company=%(company)s"
if filters.get("supplier"):
conditions += " and supplier = %(supplier)s"
if filters.get("from_date"):
conditions += " and posting_date>=%(from_date)s"
if filters.get("to_date"):
conditions += " and posting_date<=%(to_date)s"
if filters.get("mode_of_payment"):
conditions += " and ifnull(mode_of_payment, '') = %(mode_of_payment)s"
if filters.get("cost_center"):
conditions += """ and exists(select name from `tabPurchase Invoice Item`
where parent=`tabPurchase Invoice`.name
and ifnull(`tabPurchase Invoice Item`.cost_center, '') = %(cost_center)s)"""
if filters.get("warehouse"):
conditions += """ and exists(select name from `tabPurchase Invoice Item`
where parent=`tabPurchase Invoice`.name
and ifnull(`tabPurchase Invoice Item`.warehouse, '') = %(warehouse)s)"""
if filters.get("item_group"):
conditions += """ and exists(select name from `tabPurchase Invoice Item`
where parent=`tabPurchase Invoice`.name
and ifnull(`tabPurchase Invoice Item`.item_group, '') = %(item_group)s)"""
accounting_dimensions = get_accounting_dimensions(as_list=False)
if accounting_dimensions:
common_condition = """
and exists(select name from `tabPurchase Invoice Item`
where parent=`tabPurchase Invoice`.name
"""
for dimension in accounting_dimensions:
if filters.get(dimension.fieldname):
if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
filters[dimension.fieldname] = get_dimension_with_children(
dimension.document_type, filters.get(dimension.fieldname)
)
conditions += (
common_condition
+ "and ifnull(`tabPurchase Invoice`.{0}, '') in %({0})s)".format(dimension.fieldname)
)
else:
conditions += (
common_condition
+ "and ifnull(`tabPurchase Invoice`.{0}, '') in %({0})s)".format(dimension.fieldname)
)
return conditions
return columns, accounts
def get_invoices(filters, additional_query_columns):
conditions = get_conditions(filters)
return frappe.db.sql(
"""
select
name, posting_date, credit_to, supplier, supplier_name, tax_id, bill_no, bill_date,
remarks, base_net_total, base_grand_total, outstanding_amount,
mode_of_payment {0}
from `tabPurchase Invoice`
where docstatus = 1 {1}
order by posting_date desc, name desc""".format(
additional_query_columns, conditions
),
filters,
as_dict=1,
pi = frappe.qb.DocType("Purchase Invoice")
query = (
frappe.qb.from_(pi)
.select(
ConstantColumn("Purchase Invoice").as_("doctype"),
pi.name,
pi.posting_date,
pi.credit_to,
pi.supplier,
pi.supplier_name,
pi.tax_id,
pi.bill_no,
pi.bill_date,
pi.remarks,
pi.base_net_total,
pi.base_grand_total,
pi.base_rounded_total,
pi.outstanding_amount,
pi.mode_of_payment,
)
.where((pi.docstatus == 1))
.orderby(pi.posting_date, pi.name, order=Order.desc)
)
if additional_query_columns:
for col in additional_query_columns:
query = query.select(col)
if filters.get("supplier"):
query = query.where(pi.supplier == filters.supplier)
query = get_conditions(filters, query, "Purchase Invoice")
query = apply_common_conditions(
filters, query, doctype="Purchase Invoice", child_doctype="Purchase Invoice Item"
)
invoices = query.run(as_dict=True)
return invoices
def get_conditions(filters, query, doctype):
parent_doc = frappe.qb.DocType(doctype)
if filters.get("mode_of_payment"):
query = query.where(parent_doc.mode_of_payment == filters.mode_of_payment)
return query
def get_payments(filters):
args = frappe._dict(
account="credit_to",
account_fieldname="paid_to",
party="supplier",
party_name="supplier_name",
party_account=[get_party_account("Supplier", filters.supplier, filters.company)],
)
payment_entries = get_payment_entries(filters, args)
journal_entries = get_journal_entries(filters, args)
return payment_entries + journal_entries
def get_invoice_expense_map(invoice_list):
expense_details = frappe.db.sql(
@@ -300,7 +481,9 @@ def get_internal_invoice_map(invoice_list):
return internal_invoice_map
def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts):
def get_invoice_tax_map(
invoice_list, invoice_expense_map, expense_accounts, include_payments=False
):
tax_details = frappe.db.sql(
"""
select parent, account_head, case add_deduct_tax when "Add" then sum(base_tax_amount_after_discount_amount)
@@ -315,6 +498,9 @@ def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts):
as_dict=1,
)
if include_payments:
tax_details += get_advance_taxes_and_charges(invoice_list)
invoice_tax_map = {}
for d in tax_details:
if d.account_head in expense_accounts:
@@ -382,17 +568,3 @@ def get_account_details(invoice_list):
account_map[acc.name] = acc.parent_account
return account_map
def get_supplier_details(suppliers):
supplier_details = {}
for supp in frappe.db.sql(
"""select name, supplier_group from `tabSupplier`
where name in (%s)"""
% ", ".join(["%s"] * len(suppliers)),
tuple(suppliers),
as_dict=1,
):
supplier_details.setdefault(supp.name, supp.supplier_group)
return supplier_details

View File

@@ -0,0 +1,128 @@
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_months, getdate, today
from erpnext.accounts.report.purchase_register.purchase_register import execute
class TestPurchaseRegister(FrappeTestCase):
def test_purchase_register(self):
frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company 6'")
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 6'")
filters = frappe._dict(
company="_Test Company 6", from_date=add_months(today(), -1), to_date=today()
)
pi = make_purchase_invoice()
report_results = execute(filters)
first_row = frappe._dict(report_results[1][0])
self.assertEqual(first_row.voucher_type, "Purchase Invoice")
self.assertEqual(first_row.voucher_no, pi.name)
self.assertEqual(first_row.payable_account, "Creditors - _TC6")
self.assertEqual(first_row.net_total, 1000)
self.assertEqual(first_row.total_tax, 100)
self.assertEqual(first_row.grand_total, 1100)
def test_purchase_register_ledger_view(self):
frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company 6'")
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 6'")
filters = frappe._dict(
company="_Test Company 6",
from_date=add_months(today(), -1),
to_date=today(),
include_payments=True,
supplier="_Test Supplier",
)
pi = make_purchase_invoice()
pe = make_payment_entry()
report_results = execute(filters)
first_row = frappe._dict(report_results[1][2])
self.assertEqual(first_row.voucher_type, "Payment Entry")
self.assertEqual(first_row.voucher_no, pe.name)
self.assertEqual(first_row.payable_account, "Creditors - _TC6")
self.assertEqual(first_row.debit, 0)
self.assertEqual(first_row.credit, 600)
self.assertEqual(first_row.balance, 500)
def make_purchase_invoice():
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
gst_acc = create_account(
account_name="GST",
account_type="Tax",
parent_account="Duties and Taxes - _TC6",
company="_Test Company 6",
account_currency="INR",
)
create_warehouse(warehouse_name="_Test Warehouse - _TC6", company="_Test Company 6")
create_cost_center(cost_center_name="_Test Cost Center", company="_Test Company 6")
pi = create_purchase_invoice_with_taxes()
pi.submit()
return pi
def create_purchase_invoice_with_taxes():
return frappe.get_doc(
{
"doctype": "Purchase Invoice",
"posting_date": today(),
"supplier": "_Test Supplier",
"company": "_Test Company 6",
"cost_center": "_Test Cost Center - _TC6",
"taxes_and_charges": "",
"currency": "INR",
"credit_to": "Creditors - _TC6",
"items": [
{
"doctype": "Purchase Invoice Item",
"cost_center": "_Test Cost Center - _TC6",
"item_code": "_Test Item",
"qty": 1,
"rate": 1000,
"expense_account": "Stock Received But Not Billed - _TC6",
}
],
"taxes": [
{
"account_head": "GST - _TC6",
"cost_center": "_Test Cost Center - _TC6",
"add_deduct_tax": "Add",
"category": "Valuation and Total",
"charge_type": "Actual",
"description": "Shipping Charges",
"doctype": "Purchase Taxes and Charges",
"parentfield": "taxes",
"rate": 100,
"tax_amount": 100.0,
}
],
}
)
def make_payment_entry():
frappe.set_user("Administrator")
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
return create_payment_entry(
company="_Test Company 6",
party_type="Supplier",
party="_Test Supplier",
payment_type="Pay",
paid_from="Cash - _TC6",
paid_to="Creditors - _TC6",
paid_amount=600,
save=1,
submit=1,
)

View File

@@ -64,6 +64,12 @@ frappe.query_reports["Sales Register"] = {
"label": __("Item Group"),
"fieldtype": "Link",
"options": "Item Group"
},
{
"fieldname": "include_payments",
"label": __("Show Ledger View"),
"fieldtype": "Check",
"default": 0
}
]
}

View File

@@ -5,13 +5,22 @@
import frappe
from frappe import _, msgprint
from frappe.model.meta import get_field_precision
from frappe.utils import flt
from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt, getdate
from pypika import Order
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
get_dimension_with_children,
from erpnext.accounts.party import get_party_account
from erpnext.accounts.report.utils import (
apply_common_conditions,
get_advance_taxes_and_charges,
get_journal_entries,
get_opening_row,
get_party_details,
get_payment_entries,
get_query_columns,
get_taxes_query,
get_values_for_columns,
)
from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns
def execute(filters=None):
@@ -22,9 +31,15 @@ def _execute(filters, additional_table_columns=None):
if not filters:
filters = frappe._dict({})
include_payments = filters.get("include_payments")
if filters.get("include_payments") and not filters.get("customer"):
frappe.throw(_("Please select a customer for fetching payments."))
invoice_list = get_invoices(filters, get_query_columns(additional_table_columns))
columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns(
invoice_list, additional_table_columns
if filters.get("include_payments"):
invoice_list += get_payments(filters)
columns, income_accounts, unrealized_profit_loss_accounts, tax_accounts = get_columns(
invoice_list, additional_table_columns, include_payments
)
if not invoice_list:
@@ -34,13 +49,29 @@ def _execute(filters, additional_table_columns=None):
invoice_income_map = get_invoice_income_map(invoice_list)
internal_invoice_map = get_internal_invoice_map(invoice_list)
invoice_income_map, invoice_tax_map = get_invoice_tax_map(
invoice_list, invoice_income_map, income_accounts
invoice_list, invoice_income_map, income_accounts, include_payments
)
# Cost Center & Warehouse Map
invoice_cc_wh_map = get_invoice_cc_wh_map(invoice_list)
invoice_so_dn_map = get_invoice_so_dn_map(invoice_list)
company_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency")
mode_of_payments = get_mode_of_payments([inv.name for inv in invoice_list])
customers = list(set(d.customer for d in invoice_list))
customer_details = get_party_details("Customer", customers)
res = []
if include_payments:
opening_row = get_opening_row(
"Customer", filters.customer, getdate(filters.from_date), filters.company
)[0]
res.append(
{
"receivable_account": opening_row.account,
"debit": flt(opening_row.debit),
"credit": flt(opening_row.credit),
"balance": flt(opening_row.balance),
}
)
data = []
for inv in invoice_list:
@@ -51,14 +82,15 @@ def _execute(filters, additional_table_columns=None):
warehouse = list(set(invoice_cc_wh_map.get(inv.name, {}).get("warehouse", [])))
row = {
"invoice": inv.name,
"voucher_type": inv.doctype,
"voucher_no": inv.name,
"posting_date": inv.posting_date,
"customer": inv.customer,
"customer_name": inv.customer_name,
**get_values_for_columns(additional_table_columns, inv),
"customer_group": inv.get("customer_group"),
"territory": inv.get("territory"),
"tax_id": inv.get("tax_id"),
"customer_group": customer_details.get(inv.customer).get("customer_group"),
"territory": customer_details.get(inv.customer).get("territory"),
"tax_id": customer_details.get(inv.customer).get("tax_id"),
"receivable_account": inv.debit_to,
"mode_of_payment": ", ".join(mode_of_payments.get(inv.name, [])),
"project": inv.project,
@@ -66,7 +98,7 @@ def _execute(filters, additional_table_columns=None):
"remarks": inv.remarks,
"sales_order": ", ".join(sales_order),
"delivery_note": ", ".join(delivery_note),
"cost_center": ", ".join(cost_center),
"cost_center": ", ".join(cost_center) if inv.doctype == "Sales Invoice" else inv.cost_center,
"warehouse": ", ".join(warehouse),
"currency": company_currency,
}
@@ -116,19 +148,36 @@ def _execute(filters, additional_table_columns=None):
}
)
if inv.doctype == "Sales Invoice":
row.update({"debit": inv.base_grand_total, "credit": 0.0})
else:
row.update({"debit": 0.0, "credit": inv.base_grand_total})
data.append(row)
return columns, data
res += sorted(data, key=lambda x: x["posting_date"])
if include_payments:
running_balance = flt(opening_row.balance)
for row in range(1, len(res)):
running_balance += res[row]["debit"] - res[row]["credit"]
res[row].update({"balance": running_balance})
return columns, res, None, None, None, include_payments
def get_columns(invoice_list, additional_table_columns):
def get_columns(invoice_list, additional_table_columns, include_payments=False):
"""return columns based on filters"""
columns = [
{
"label": _("Invoice"),
"fieldname": "invoice",
"fieldtype": "Link",
"options": "Sales Invoice",
"label": _("Voucher Type"),
"fieldname": "voucher_type",
"width": 120,
},
{
"label": _("Voucher"),
"fieldname": "voucher_no",
"fieldtype": "Dynamic Link",
"options": "voucher_type",
"width": 120,
},
{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 80},
@@ -142,83 +191,156 @@ def get_columns(invoice_list, additional_table_columns):
{"label": _("Customer Name"), "fieldname": "customer_name", "fieldtype": "Data", "width": 120},
]
if additional_table_columns:
if additional_table_columns and not include_payments:
columns += additional_table_columns
columns += [
if not include_payments:
columns += [
{
"label": _("Customer Group"),
"fieldname": "customer_group",
"fieldtype": "Link",
"options": "Customer Group",
"width": 120,
},
{
"label": _("Territory"),
"fieldname": "territory",
"fieldtype": "Link",
"options": "Territory",
"width": 80,
},
{"label": _("Tax Id"), "fieldname": "tax_id", "fieldtype": "Data", "width": 80},
{
"label": _("Receivable Account"),
"fieldname": "receivable_account",
"fieldtype": "Link",
"options": "Account",
"width": 100,
},
{
"label": _("Mode Of Payment"),
"fieldname": "mode_of_payment",
"fieldtype": "Data",
"width": 120,
},
{
"label": _("Project"),
"fieldname": "project",
"fieldtype": "Link",
"options": "Project",
"width": 80,
},
{"label": _("Owner"), "fieldname": "owner", "fieldtype": "Data", "width": 100},
{
"label": _("Sales Order"),
"fieldname": "sales_order",
"fieldtype": "Link",
"options": "Sales Order",
"width": 100,
},
{
"label": _("Delivery Note"),
"fieldname": "delivery_note",
"fieldtype": "Link",
"options": "Delivery Note",
"width": 100,
},
{
"label": _("Cost Center"),
"fieldname": "cost_center",
"fieldtype": "Link",
"options": "Cost Center",
"width": 100,
},
{
"label": _("Warehouse"),
"fieldname": "warehouse",
"fieldtype": "Link",
"options": "Warehouse",
"width": 100,
},
{"fieldname": "currency", "label": _("Currency"), "fieldtype": "Data", "width": 80},
]
else:
columns += [
{
"fieldname": "receivable_account",
"label": _("Receivable Account"),
"fieldtype": "Link",
"options": "Account",
"width": 120,
},
{"fieldname": "debit", "label": _("Debit"), "fieldtype": "Currency", "width": 120},
{"fieldname": "credit", "label": _("Credit"), "fieldtype": "Currency", "width": 120},
{"fieldname": "balance", "label": _("Balance"), "fieldtype": "Currency", "width": 120},
]
account_columns, accounts = get_account_columns(invoice_list, include_payments)
net_total_column = [
{
"label": _("Customer Group"),
"fieldname": "customer_group",
"fieldtype": "Link",
"options": "Customer Group",
"label": _("Net Total"),
"fieldname": "net_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Territory"),
"fieldname": "territory",
"fieldtype": "Link",
"options": "Territory",
"width": 80,
},
{"label": _("Tax Id"), "fieldname": "tax_id", "fieldtype": "Data", "width": 120},
{
"label": _("Receivable Account"),
"fieldname": "receivable_account",
"fieldtype": "Link",
"options": "Account",
"width": 80,
},
{
"label": _("Mode Of Payment"),
"fieldname": "mode_of_payment",
"fieldtype": "Data",
"width": 120,
},
{
"label": _("Project"),
"fieldname": "project",
"fieldtype": "Link",
"options": "Project",
"width": 80,
},
{"label": _("Owner"), "fieldname": "owner", "fieldtype": "Data", "width": 150},
{"label": _("Remarks"), "fieldname": "remarks", "fieldtype": "Data", "width": 150},
{
"label": _("Sales Order"),
"fieldname": "sales_order",
"fieldtype": "Link",
"options": "Sales Order",
"width": 100,
},
{
"label": _("Delivery Note"),
"fieldname": "delivery_note",
"fieldtype": "Link",
"options": "Delivery Note",
"width": 100,
},
{
"label": _("Cost Center"),
"fieldname": "cost_center",
"fieldtype": "Link",
"options": "Cost Center",
"width": 100,
},
{
"label": _("Warehouse"),
"fieldname": "warehouse",
"fieldtype": "Link",
"options": "Warehouse",
"width": 100,
},
{"fieldname": "currency", "label": _("Currency"), "fieldtype": "Data", "width": 80},
}
]
total_columns = [
{
"label": _("Tax Total"),
"fieldname": "tax_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
]
if not include_payments:
total_columns += [
{
"label": _("Grand Total"),
"fieldname": "grand_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Rounded Total"),
"fieldname": "rounded_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Outstanding Amount"),
"fieldname": "outstanding_amount",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
]
columns = (
columns
+ account_columns[0]
+ account_columns[2]
+ net_total_column
+ account_columns[1]
+ total_columns
)
columns += [{"label": _("Remarks"), "fieldname": "remarks", "fieldtype": "Data", "width": 150}]
return columns, accounts[0], accounts[1], accounts[2]
def get_account_columns(invoice_list, include_payments):
income_accounts = []
tax_accounts = []
unrealized_profit_loss_accounts = []
income_columns = []
tax_columns = []
unrealized_profit_loss_accounts = []
unrealized_profit_loss_account_columns = []
if invoice_list:
@@ -230,14 +352,16 @@ def get_columns(invoice_list, additional_table_columns):
tuple(inv.name for inv in invoice_list),
)
tax_accounts = frappe.db.sql_list(
"""select distinct account_head
from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice'
and docstatus = 1 and base_tax_amount_after_discount_amount != 0
and parent in (%s) order by account_head"""
% ", ".join(["%s"] * len(invoice_list)),
tuple(inv.name for inv in invoice_list),
)
sales_taxes_query = get_taxes_query(invoice_list, "Sales Taxes and Charges", "Sales Invoice")
sales_tax_accounts = sales_taxes_query.run(as_dict=True, pluck="account_head")
tax_accounts = sales_tax_accounts
if include_payments:
advance_taxes_query = get_taxes_query(
invoice_list, "Advance Taxes and Charges", "Payment Entry"
)
advance_tax_accounts = advance_taxes_query.run(as_dict=True, pluck="account_head")
tax_accounts = set(tax_accounts + advance_tax_accounts)
unrealized_profit_loss_accounts = frappe.db.sql_list(
"""SELECT distinct unrealized_profit_loss_account
@@ -283,134 +407,82 @@ def get_columns(invoice_list, additional_table_columns):
}
)
net_total_column = [
{
"label": _("Net Total"),
"fieldname": "net_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
]
columns = [income_columns, unrealized_profit_loss_account_columns, tax_columns]
accounts = [income_accounts, unrealized_profit_loss_accounts, tax_accounts]
total_columns = [
{
"label": _("Tax Total"),
"fieldname": "tax_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Grand Total"),
"fieldname": "grand_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Rounded Total"),
"fieldname": "rounded_total",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Outstanding Amount"),
"fieldname": "outstanding_amount",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
]
columns = (
columns
+ income_columns
+ unrealized_profit_loss_account_columns
+ net_total_column
+ tax_columns
+ total_columns
)
return columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts
def get_conditions(filters):
conditions = ""
accounting_dimensions = get_accounting_dimensions(as_list=False) or []
accounting_dimensions_list = [d.fieldname for d in accounting_dimensions]
if filters.get("company"):
conditions += " and company=%(company)s"
if filters.get("customer") and "customer" not in accounting_dimensions_list:
conditions += " and customer = %(customer)s"
if filters.get("from_date"):
conditions += " and posting_date >= %(from_date)s"
if filters.get("to_date"):
conditions += " and posting_date <= %(to_date)s"
if filters.get("owner"):
conditions += " and owner = %(owner)s"
def get_sales_invoice_item_field_condition(field, table="Sales Invoice Item") -> str:
if not filters.get(field) or field in accounting_dimensions_list:
return ""
return f""" and exists(select name from `tab{table}`
where parent=`tabSales Invoice`.name
and ifnull(`tab{table}`.{field}, '') = %({field})s)"""
conditions += get_sales_invoice_item_field_condition("mode_of_payment", "Sales Invoice Payment")
conditions += get_sales_invoice_item_field_condition("cost_center")
conditions += get_sales_invoice_item_field_condition("warehouse")
conditions += get_sales_invoice_item_field_condition("brand")
conditions += get_sales_invoice_item_field_condition("item_group")
if accounting_dimensions:
common_condition = """
and exists(select name from `tabSales Invoice Item`
where parent=`tabSales Invoice`.name
"""
for dimension in accounting_dimensions:
if filters.get(dimension.fieldname):
if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
filters[dimension.fieldname] = get_dimension_with_children(
dimension.document_type, filters.get(dimension.fieldname)
)
conditions += (
common_condition
+ "and ifnull(`tabSales Invoice`.{0}, '') in %({0})s)".format(dimension.fieldname)
)
else:
conditions += (
common_condition
+ "and ifnull(`tabSales Invoice`.{0}, '') in %({0})s)".format(dimension.fieldname)
)
return conditions
return columns, accounts
def get_invoices(filters, additional_query_columns):
conditions = get_conditions(filters)
return frappe.db.sql(
"""
select name, posting_date, debit_to, project, customer,
customer_name, owner, remarks, territory, tax_id, customer_group,
base_net_total, base_grand_total, base_rounded_total, outstanding_amount,
is_internal_customer, represents_company, company {0}
from `tabSales Invoice`
where docstatus = 1 {1}
order by posting_date desc, name desc""".format(
additional_query_columns, conditions
),
filters,
as_dict=1,
si = frappe.qb.DocType("Sales Invoice")
query = (
frappe.qb.from_(si)
.select(
ConstantColumn("Sales Invoice").as_("doctype"),
si.name,
si.posting_date,
si.debit_to,
si.project,
si.customer,
si.customer_name,
si.owner,
si.remarks,
si.territory,
si.tax_id,
si.customer_group,
si.base_net_total,
si.base_grand_total,
si.base_rounded_total,
si.outstanding_amount,
si.is_internal_customer,
si.represents_company,
si.company,
)
.where((si.docstatus == 1))
.orderby(si.posting_date, si.name, order=Order.desc)
)
if additional_query_columns:
for col in additional_query_columns:
query = query.select(col)
if filters.get("customer"):
query = query.where(si.customer == filters.customer)
query = get_conditions(filters, query, "Sales Invoice")
query = apply_common_conditions(
filters, query, doctype="Sales Invoice", child_doctype="Sales Invoice Item"
)
invoices = query.run(as_dict=True)
return invoices
def get_conditions(filters, query, doctype):
parent_doc = frappe.qb.DocType(doctype)
if filters.get("owner"):
query = query.where(parent_doc.owner == filters.owner)
if filters.get("mode_of_payment"):
payment_doc = frappe.qb.DocType("Sales Invoice Payment")
query = query.inner_join(payment_doc).on(parent_doc.name == payment_doc.parent)
query = query.where(payment_doc.mode_of_payment == filters.mode_of_payment).distinct()
return query
def get_payments(filters):
args = frappe._dict(
account="debit_to",
account_fieldname="paid_from",
party="customer",
party_name="customer_name",
party_account=[get_party_account("Customer", filters.customer, filters.company)],
)
payment_entries = get_payment_entries(filters, args)
journal_entries = get_journal_entries(filters, args)
return payment_entries + journal_entries
def get_invoice_income_map(invoice_list):
income_details = frappe.db.sql(
@@ -447,7 +519,7 @@ def get_internal_invoice_map(invoice_list):
return internal_invoice_map
def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts):
def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts, include_payments=False):
tax_details = frappe.db.sql(
"""select parent, account_head,
sum(base_tax_amount_after_discount_amount) as tax_amount
@@ -457,6 +529,9 @@ def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts):
as_dict=1,
)
if include_payments:
tax_details += get_advance_taxes_and_charges(invoice_list)
invoice_tax_map = {}
for d in tax_details:
if d.account_head in income_accounts:

View File

@@ -46,12 +46,10 @@ def get_result(
out = []
for name, details in gle_map.items():
tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0
bill_no, bill_date = "", ""
tax_withholding_category = tax_category_map.get(name)
rate = tax_rate_map.get(tax_withholding_category)
for entry in details:
tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0
tax_withholding_category, rate = None, None
bill_no, bill_date = "", ""
party = entry.party or entry.against
posting_date = entry.posting_date
voucher_type = entry.voucher_type
@@ -61,12 +59,19 @@ def get_result(
if party_list:
party = party_list[0]
if not tax_withholding_category:
tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category")
rate = tax_rate_map.get(tax_withholding_category)
if entry.account in tds_accounts:
if entry.account in tds_accounts.keys():
tax_amount += entry.credit - entry.debit
# infer tax withholding category from the account if it's the single account for this category
tax_withholding_category = tds_accounts.get(entry.account)
rate = tax_rate_map.get(tax_withholding_category)
# or else the consolidated value from the voucher document
if not tax_withholding_category:
# or else from the party default
tax_withholding_category = tax_category_map.get(name)
rate = tax_rate_map.get(tax_withholding_category)
if not tax_withholding_category:
tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category")
rate = tax_rate_map.get(tax_withholding_category)
if net_total_map.get(name):
if voucher_type == "Journal Entry" and tax_amount and rate:
@@ -80,41 +85,41 @@ def get_result(
else:
total_amount += entry.credit
if tax_amount:
if party_map.get(party, {}).get("party_type") == "Supplier":
party_name = "supplier_name"
party_type = "supplier_type"
else:
party_name = "customer_name"
party_type = "customer_type"
if tax_amount:
if party_map.get(party, {}).get("party_type") == "Supplier":
party_name = "supplier_name"
party_type = "supplier_type"
else:
party_name = "customer_name"
party_type = "customer_type"
row = {
"pan"
if frappe.db.has_column(filters.party_type, "pan")
else "tax_id": party_map.get(party, {}).get("pan"),
"party": party_map.get(party, {}).get("name"),
}
if filters.naming_series == "Naming Series":
row.update({"party_name": party_map.get(party, {}).get(party_name)})
row.update(
{
"section_code": tax_withholding_category or "",
"entity_type": party_map.get(party, {}).get(party_type),
"rate": rate,
"total_amount": total_amount,
"grand_total": grand_total,
"base_total": base_total,
"tax_amount": tax_amount,
"transaction_date": posting_date,
"transaction_type": voucher_type,
"ref_no": name,
"supplier_invoice_no": bill_no,
"supplier_invoice_date": bill_date,
row = {
"pan"
if frappe.db.has_column(filters.party_type, "pan")
else "tax_id": party_map.get(party, {}).get("pan"),
"party": party_map.get(party, {}).get("name"),
}
)
out.append(row)
if filters.naming_series == "Naming Series":
row.update({"party_name": party_map.get(party, {}).get(party_name)})
row.update(
{
"section_code": tax_withholding_category or "",
"entity_type": party_map.get(party, {}).get(party_type),
"rate": rate,
"total_amount": total_amount,
"grand_total": grand_total,
"base_total": base_total,
"tax_amount": tax_amount,
"transaction_date": posting_date,
"transaction_type": voucher_type,
"ref_no": name,
"supplier_invoice_no": bill_no,
"supplier_invoice_date": bill_date,
}
)
out.append(row)
out.sort(key=lambda x: x["section_code"])
@@ -184,6 +189,16 @@ def get_columns(filters):
"width": 180,
}
)
else:
columns.append(
{
"label": _(filters.get("party_type")),
"fieldname": "party",
"fieldtype": "Dynamic Link",
"options": "party_type",
"width": 180,
}
)
columns.extend(
[
@@ -272,11 +287,20 @@ def get_tds_docs(filters):
journal_entry_party_map = frappe._dict()
bank_accounts = frappe.get_all("Account", {"is_group": 0, "account_type": "Bank"}, pluck="name")
tds_accounts = frappe.get_all(
"Tax Withholding Account", {"company": filters.get("company")}, pluck="account"
_tds_accounts = frappe.get_all(
"Tax Withholding Account",
{"company": filters.get("company")},
["account", "parent"],
)
tds_accounts = {}
for tds_acc in _tds_accounts:
# if it turns out not to be the only tax withholding category, then don't include in the map
if tds_accounts.get(tds_acc["account"]):
tds_accounts[tds_acc["account"]] = None
else:
tds_accounts[tds_acc["account"]] = tds_acc["parent"]
tds_docs = get_tds_docs_query(filters, bank_accounts, tds_accounts).run(as_dict=True)
tds_docs = get_tds_docs_query(filters, bank_accounts, list(tds_accounts.keys())).run(as_dict=True)
for d in tds_docs:
if d.voucher_type == "Purchase Invoice":
@@ -335,21 +359,16 @@ def get_tds_docs_query(filters, bank_accounts, tds_accounts):
if filters.get("party"):
party = [filters.get("party")]
query = query.where(
((gle.account.isin(tds_accounts) & gle.against.isin(party)))
| ((gle.voucher_type == "Journal Entry") & (gle.party == filters.get("party")))
| gle.party.isin(party)
jv_condition = gle.against.isin(party) | (
(gle.voucher_type == "Journal Entry") & (gle.party == filters.get("party"))
)
else:
party = frappe.get_all(filters.get("party_type"), pluck="name")
query = query.where(
((gle.account.isin(tds_accounts) & gle.against.isin(party)))
| (
(gle.voucher_type == "Journal Entry")
& ((gle.party_type == filters.get("party_type")) | (gle.party_type == ""))
)
| gle.party.isin(party)
jv_condition = gle.against.isin(party) | (
(gle.voucher_type == "Journal Entry")
& ((gle.party_type == filters.get("party_type")) | (gle.party_type == ""))
)
query = query.where((gle.account.isin(tds_accounts) & jv_condition) | gle.party.isin(party))
return query

View File

@@ -1,8 +1,16 @@
import frappe
from frappe.query_builder.custom import ConstantColumn
from frappe.query_builder.functions import Sum
from frappe.utils import flt, formatdate, get_datetime_str, get_table_name
from pypika import Order
from erpnext import get_company_currency, get_default_company
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
get_dimension_with_children,
)
from erpnext.accounts.doctype.fiscal_year.fiscal_year import get_from_and_to_date
from erpnext.accounts.party import get_party_account
from erpnext.setup.utils import get_exchange_rate
__exchange_rates = {}
@@ -165,7 +173,7 @@ def get_query_columns(report_columns):
else:
columns.append(fieldname)
return ", " + ", ".join(columns)
return columns
def get_values_for_columns(report_columns, report_row):
@@ -179,3 +187,206 @@ def get_values_for_columns(report_columns, report_row):
values[fieldname] = report_row.get(fieldname)
return values
def get_party_details(party_type, party_list):
party_details = {}
party = frappe.qb.DocType(party_type)
query = frappe.qb.from_(party).select(party.name, party.tax_id).where(party.name.isin(party_list))
if party_type == "Supplier":
query = query.select(party.supplier_group)
else:
query = query.select(party.customer_group, party.territory)
party_detail_list = query.run(as_dict=True)
for party_dict in party_detail_list:
party_details[party_dict.name] = party_dict
return party_details
def get_taxes_query(invoice_list, doctype, parenttype):
taxes = frappe.qb.DocType(doctype)
query = (
frappe.qb.from_(taxes)
.select(taxes.account_head)
.distinct()
.where(
(taxes.parenttype == parenttype)
& (taxes.docstatus == 1)
& (taxes.account_head.isnotnull())
& (taxes.parent.isin([inv.name for inv in invoice_list]))
)
.orderby(taxes.account_head)
)
if doctype == "Purchase Taxes and Charges":
return query.where(taxes.category.isin(["Total", "Valuation and Total"]))
elif doctype == "Sales Taxes and Charges":
return query
return query.where(taxes.charge_type.isin(["On Paid Amount", "Actual"]))
def get_journal_entries(filters, args):
je = frappe.qb.DocType("Journal Entry")
journal_account = frappe.qb.DocType("Journal Entry Account")
query = (
frappe.qb.from_(je)
.inner_join(journal_account)
.on(je.name == journal_account.parent)
.select(
je.voucher_type.as_("doctype"),
je.name,
je.posting_date,
journal_account.account.as_(args.account),
journal_account.party.as_(args.party),
journal_account.party.as_(args.party_name),
je.bill_no,
je.bill_date,
je.remark.as_("remarks"),
je.total_amount.as_("base_net_total"),
je.total_amount.as_("base_grand_total"),
je.mode_of_payment,
journal_account.project,
)
.where(
(je.voucher_type == "Journal Entry")
& (je.docstatus == 1)
& (journal_account.party == filters.get(args.party))
& (journal_account.account.isin(args.party_account))
)
.orderby(je.posting_date, je.name, order=Order.desc)
)
query = apply_common_conditions(filters, query, doctype="Journal Entry", payments=True)
journal_entries = query.run(as_dict=True)
return journal_entries
def get_payment_entries(filters, args):
pe = frappe.qb.DocType("Payment Entry")
query = (
frappe.qb.from_(pe)
.select(
ConstantColumn("Payment Entry").as_("doctype"),
pe.name,
pe.posting_date,
pe[args.account_fieldname].as_(args.account),
pe.party.as_(args.party),
pe.party_name.as_(args.party_name),
pe.remarks,
pe.paid_amount.as_("base_net_total"),
pe.paid_amount_after_tax.as_("base_grand_total"),
pe.mode_of_payment,
pe.project,
pe.cost_center,
)
.where(
(pe.docstatus == 1)
& (pe.party == filters.get(args.party))
& (pe[args.account_fieldname].isin(args.party_account))
)
.orderby(pe.posting_date, pe.name, order=Order.desc)
)
query = apply_common_conditions(filters, query, doctype="Payment Entry", payments=True)
payment_entries = query.run(as_dict=True)
return payment_entries
def apply_common_conditions(filters, query, doctype, child_doctype=None, payments=False):
parent_doc = frappe.qb.DocType(doctype)
if child_doctype:
child_doc = frappe.qb.DocType(child_doctype)
join_required = False
if filters.get("company"):
query = query.where(parent_doc.company == filters.company)
if filters.get("from_date"):
query = query.where(parent_doc.posting_date >= filters.from_date)
if filters.get("to_date"):
query = query.where(parent_doc.posting_date <= filters.to_date)
if payments:
if filters.get("cost_center"):
query = query.where(parent_doc.cost_center == filters.cost_center)
else:
if filters.get("cost_center"):
query = query.where(child_doc.cost_center == filters.cost_center)
join_required = True
if filters.get("warehouse"):
query = query.where(child_doc.warehouse == filters.warehouse)
join_required = True
if filters.get("item_group"):
query = query.where(child_doc.item_group == filters.item_group)
join_required = True
if not payments:
if filters.get("brand"):
query = query.where(child_doc.brand == filters.brand)
join_required = True
if join_required:
query = query.inner_join(child_doc).on(parent_doc.name == child_doc.parent)
query = query.distinct()
if parent_doc.get_table_name() != "tabJournal Entry":
query = filter_invoices_based_on_dimensions(filters, query, parent_doc)
return query
def get_advance_taxes_and_charges(invoice_list):
adv_taxes = frappe.qb.DocType("Advance Taxes and Charges")
return (
frappe.qb.from_(adv_taxes)
.select(
adv_taxes.parent,
adv_taxes.account_head,
(
frappe.qb.terms.Case()
.when(adv_taxes.add_deduct_tax == "Add", Sum(adv_taxes.base_tax_amount))
.else_(Sum(adv_taxes.base_tax_amount) * -1)
).as_("tax_amount"),
)
.where(
(adv_taxes.parent.isin([inv.name for inv in invoice_list]))
& (adv_taxes.charge_type.isin(["On Paid Amount", "Actual"]))
& (adv_taxes.base_tax_amount != 0)
)
.groupby(adv_taxes.parent, adv_taxes.account_head, adv_taxes.add_deduct_tax)
).run(as_dict=True)
def filter_invoices_based_on_dimensions(filters, query, parent_doc):
accounting_dimensions = get_accounting_dimensions(as_list=False)
if accounting_dimensions:
for dimension in accounting_dimensions:
if filters.get(dimension.fieldname):
if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"):
filters[dimension.fieldname] = get_dimension_with_children(
dimension.document_type, filters.get(dimension.fieldname)
)
fieldname = dimension.fieldname
query = query.where(parent_doc[fieldname].isin(filters[fieldname]))
return query
def get_opening_row(party_type, party, from_date, company):
party_account = get_party_account(party_type, party, company)
gle = frappe.qb.DocType("GL Entry")
return (
frappe.qb.from_(gle)
.select(
ConstantColumn("Opening").as_("account"),
Sum(gle.debit).as_("debit"),
Sum(gle.credit).as_("credit"),
(Sum(gle.debit) - Sum(gle.credit)).as_("balance"),
)
.where(
(gle.account == party_account)
& (gle.party == party)
& (gle.posting_date < from_date)
& (gle.is_cancelled == 0)
)
).run(as_dict=True)

View File

@@ -22,6 +22,10 @@ class TestUtils(unittest.TestCase):
super(TestUtils, cls).setUpClass()
make_test_objects("Address", ADDRESS_RECORDS)
@classmethod
def tearDownClass(cls):
frappe.db.rollback()
def test_get_party_shipping_address(self):
address = get_party_shipping_address("Customer", "_Test Customer 1")
self.assertEqual(address, "_Test Billing Address 2 Title-Billing")
@@ -125,6 +129,38 @@ class TestUtils(unittest.TestCase):
self.assertEqual(len(payment_entry.references), 1)
self.assertEqual(payment_entry.difference_amount, 0)
def test_naming_series_variable_parsing(self):
"""
Tests parsing utility used by Naming Series Variable hook for FY
"""
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from frappe.utils import nowdate
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
# Configure Supplier Naming in Buying Settings
frappe.db.set_default("supp_master_name", "Auto Name")
# Configure Autoname in Supplier DocType
make_property_setter(
"Supplier", None, "naming_rule", "Expression", "Data", for_doctype="Doctype"
)
make_property_setter(
"Supplier", None, "autoname", "SUP-.FY.-.#####", "Data", for_doctype="Doctype"
)
fiscal_year = get_fiscal_year(nowdate())[0]
# Create Supplier
supplier = create_supplier()
# Check Naming Series in generated Supplier ID
doc_name = supplier.name.split("-")
self.assertEqual(len(doc_name), 3)
self.assertSequenceEqual(doc_name[0:2], ("SUP", fiscal_year))
frappe.db.set_default("supp_master_name", "Supplier Name")
ADDRESS_RECORDS = [
{

View File

@@ -179,6 +179,7 @@ def get_balance_on(
in_account_currency=True,
cost_center=None,
ignore_account_permission=False,
start_date=None,
):
if not account and frappe.form_dict.get("account"):
account = frappe.form_dict.get("account")
@@ -192,6 +193,8 @@ def get_balance_on(
cost_center = frappe.form_dict.get("cost_center")
cond = ["is_cancelled=0"]
if start_date:
cond.append("posting_date >= %s" % frappe.db.escape(cstr(start_date)))
if date:
cond.append("posting_date <= %s" % frappe.db.escape(cstr(date)))
else:
@@ -431,7 +434,19 @@ def add_cc(args=None):
return cc.name
def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # nosemgrep
def _build_dimensions_dict_for_exc_gain_loss(
entry: dict | object = None, active_dimensions: list = None
):
dimensions_dict = frappe._dict()
if entry and active_dimensions:
for dim in active_dimensions:
dimensions_dict[dim.fieldname] = entry.get(dim.fieldname)
return dimensions_dict
def reconcile_against_document(
args, skip_ref_details_update_for_pe=False, active_dimensions=None
): # nosemgrep
"""
Cancel PE or JV, Update against document, split if required and resubmit
"""
@@ -456,6 +471,8 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n
check_if_advance_entry_modified(entry)
validate_allocated_amount(entry)
dimensions_dict = _build_dimensions_dict_for_exc_gain_loss(entry, active_dimensions)
# update ref in advance entry
if voucher_type == "Journal Entry":
referenced_row = update_reference_in_journal_entry(entry, doc, do_not_save=False)
@@ -463,10 +480,14 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n
# amount and account in args
# referenced_row is used to deduplicate gain/loss journal
entry.update({"referenced_row": referenced_row})
doc.make_exchange_gain_loss_journal([entry])
doc.make_exchange_gain_loss_journal([entry], dimensions_dict)
else:
update_reference_in_payment_entry(
entry, doc, do_not_save=True, skip_ref_details_update_for_pe=skip_ref_details_update_for_pe
referenced_row = update_reference_in_payment_entry(
entry,
doc,
do_not_save=True,
skip_ref_details_update_for_pe=skip_ref_details_update_for_pe,
dimensions_dict=dimensions_dict,
)
doc.save(ignore_permissions=True)
@@ -533,7 +554,7 @@ def check_if_advance_entry_modified(args):
where
name = %(voucher_no)s and docstatus = 1
and party_type = %(party_type)s and party = %(party)s and {0} = %(account)s
and round(unallocated_amount, {1}) = %(unreconciled_amount)s
and round(unallocated_amount, {1}) = round(%(unreconciled_amount)s, {1})
""".format(
party_account_field, precision
),
@@ -615,7 +636,7 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
def update_reference_in_payment_entry(
d, payment_entry, do_not_save=False, skip_ref_details_update_for_pe=False
d, payment_entry, do_not_save=False, skip_ref_details_update_for_pe=False, dimensions_dict=None
):
reference_details = {
"reference_doctype": d.against_voucher_type,
@@ -623,8 +644,12 @@ def update_reference_in_payment_entry(
"total_amount": d.grand_total,
"outstanding_amount": d.outstanding_amount,
"allocated_amount": d.allocated_amount,
"exchange_rate": d.exchange_rate if d.exchange_gain_loss else payment_entry.get_exchange_rate(),
"exchange_gain_loss": d.exchange_gain_loss,
"exchange_rate": d.exchange_rate
if d.difference_amount is not None
else payment_entry.get_exchange_rate(),
"exchange_gain_loss": d.difference_amount,
"account": d.account,
"dimensions": d.dimensions,
}
if d.voucher_detail_no:
@@ -656,8 +681,9 @@ def update_reference_in_payment_entry(
if not skip_ref_details_update_for_pe:
payment_entry.set_missing_ref_details()
payment_entry.set_amounts()
payment_entry.make_exchange_gain_loss_journal(
frappe._dict({"difference_posting_date": d.difference_posting_date})
frappe._dict({"difference_posting_date": d.difference_posting_date}), dimensions_dict
)
if not do_not_save:
@@ -1217,8 +1243,13 @@ def get_autoname_with_number(number_value, doc_title, company):
def parse_naming_series_variable(doc, variable):
if variable == "FY":
date = doc.get("posting_date") or doc.get("transaction_date") or getdate()
return get_fiscal_year(date=date, company=doc.get("company"))[0]
if doc:
date = doc.get("posting_date") or doc.get("transaction_date") or getdate()
company = doc.get("company")
else:
date = getdate()
company = None
return get_fiscal_year(date=date, company=company)[0]
@frappe.whitelist()
@@ -1617,6 +1648,7 @@ def get_payment_ledger_entries(gl_entries, cancel=0):
due_date=gle.due_date,
voucher_type=gle.voucher_type,
voucher_no=gle.voucher_no,
voucher_detail_no=gle.voucher_detail_no,
against_voucher_type=gle.against_voucher_type
if gle.against_voucher_type
else gle.voucher_type,
@@ -1638,7 +1670,7 @@ def get_payment_ledger_entries(gl_entries, cancel=0):
def create_payment_ledger_entry(
gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0
gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0, partial_cancel=False
):
if gl_entries:
ple_map = get_payment_ledger_entries(gl_entries, cancel=cancel)
@@ -1648,7 +1680,7 @@ def create_payment_ledger_entry(
ple = frappe.get_doc(entry)
if cancel:
delink_original_entry(ple)
delink_original_entry(ple, partial_cancel=partial_cancel)
ple.flags.ignore_permissions = 1
ple.flags.adv_adj = adv_adj
@@ -1695,7 +1727,7 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa
ref_doc.set_status(update=True)
def delink_original_entry(pl_entry):
def delink_original_entry(pl_entry, partial_cancel=False):
if pl_entry:
ple = qb.DocType("Payment Ledger Entry")
query = (
@@ -1715,6 +1747,10 @@ def delink_original_entry(pl_entry):
& (ple.against_voucher_no == pl_entry.against_voucher_no)
)
)
if partial_cancel:
query = query.where(ple.voucher_detail_no == pl_entry.voucher_detail_no)
query.run()
@@ -1791,6 +1827,30 @@ class QueryPaymentLedger(object):
Table("outstanding").amount_in_account_currency >= self.max_outstanding
)
if self.limit and self.get_invoices:
outstanding_vouchers = (
qb.from_(ple)
.select(
ple.against_voucher_no.as_("voucher_no"),
Sum(ple.amount_in_account_currency).as_("amount_in_account_currency"),
)
.where(ple.delinked == 0)
.where(Criterion.all(filter_on_against_voucher_no))
.where(Criterion.all(self.common_filter))
.where(Criterion.all(self.dimensions_filter))
.where(Criterion.all(self.voucher_posting_date))
.groupby(ple.against_voucher_type, ple.against_voucher_no, ple.party_type, ple.party)
.orderby(ple.posting_date, ple.voucher_no)
.having(qb.Field("amount_in_account_currency") > 0)
.limit(self.limit)
.run()
)
if outstanding_vouchers:
filter_on_voucher_no.append(ple.voucher_no.isin([x[0] for x in outstanding_vouchers]))
filter_on_against_voucher_no.append(
ple.against_voucher_no.isin([x[0] for x in outstanding_vouchers])
)
# build query for voucher amount
query_voucher_amount = (
qb.from_(ple)
@@ -1952,6 +2012,7 @@ def create_gain_loss_journal(
ref2_dn,
ref2_detail_no,
cost_center,
dimensions,
) -> str:
journal_entry = frappe.new_doc("Journal Entry")
journal_entry.voucher_type = "Exchange Gain Or Loss"
@@ -1985,7 +2046,8 @@ def create_gain_loss_journal(
dr_or_cr + "_in_account_currency": 0,
}
)
if dimensions:
journal_account.update(dimensions)
journal_entry.append("accounts", journal_account)
journal_account = frappe._dict(
@@ -2001,7 +2063,8 @@ def create_gain_loss_journal(
reverse_dr_or_cr: abs(exc_gain_loss),
}
)
if dimensions:
journal_account.update(dimensions)
journal_entry.append("accounts", journal_account)
journal_entry.save()

View File

@@ -322,13 +322,16 @@ frappe.ui.form.on('Asset', {
},
make_schedules_editable: function(frm) {
if (frm.doc.finance_books) {
var is_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0
if (frm.doc.finance_books.length) {
var is_manual_hence_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0
? true : false;
var is_shift_hence_editable = frm.doc.finance_books.filter(d => d.shift_based).length > 0
? true : false;
frm.toggle_enable("schedules", is_editable);
frm.fields_dict["schedules"].grid.toggle_enable("schedule_date", is_editable);
frm.fields_dict["schedules"].grid.toggle_enable("depreciation_amount", is_editable);
frm.toggle_enable("schedules", is_manual_hence_editable || is_shift_hence_editable);
frm.fields_dict["schedules"].grid.toggle_enable("schedule_date", is_manual_hence_editable);
frm.fields_dict["schedules"].grid.toggle_enable("depreciation_amount", is_manual_hence_editable);
frm.fields_dict["schedules"].grid.toggle_enable("shift", is_shift_hence_editable);
}
},
@@ -516,10 +519,16 @@ frappe.ui.form.on('Asset', {
indicator: 'red'
});
}
frm.set_value('gross_purchase_amount', item.base_net_rate + item.item_tax_amount);
frm.set_value('purchase_receipt_amount', item.base_net_rate + item.item_tax_amount);
item.asset_location && frm.set_value('location', item.asset_location);
frm.set_value('cost_center', item.cost_center || purchase_doc.cost_center);
frappe.db.get_value('Item', item.item_code, 'is_grouped_asset', (r) => {
var asset_quantity = r.is_grouped_asset ? item.qty : 1;
var purchase_amount = flt(item.valuation_rate * asset_quantity, precision('gross_purchase_amount'));
frm.set_value('gross_purchase_amount', purchase_amount);
frm.set_value('purchase_receipt_amount', purchase_amount);
frm.set_value('asset_quantity', asset_quantity);
frm.set_value('cost_center', item.cost_center || purchase_doc.cost_center);
if(item.asset_location) { frm.set_value('location', item.asset_location); }
});
},
set_depreciation_rate: function(frm, row) {

View File

@@ -35,6 +35,8 @@
"purchase_receipt",
"purchase_invoice",
"available_for_use_date",
"total_asset_cost",
"additional_asset_cost",
"column_break_23",
"gross_purchase_amount",
"asset_quantity",
@@ -200,9 +202,8 @@
"fieldname": "purchase_date",
"fieldtype": "Date",
"label": "Purchase Date",
"read_only": 1,
"read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset",
"reqd": 1
"mandatory_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset",
"read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset"
},
{
"fieldname": "disposal_date",
@@ -225,15 +226,15 @@
"fieldname": "gross_purchase_amount",
"fieldtype": "Currency",
"label": "Gross Purchase Amount",
"mandatory_depends_on": "eval:(!doc.is_composite_asset || doc.docstatus==1)",
"options": "Company:company:default_currency",
"read_only_depends_on": "eval:!doc.is_existing_asset",
"reqd": 1
"read_only_depends_on": "eval:!doc.is_existing_asset"
},
{
"fieldname": "available_for_use_date",
"fieldtype": "Date",
"label": "Available-for-use Date",
"reqd": 1
"mandatory_depends_on": "eval:(!doc.is_composite_asset || doc.docstatus==1)"
},
{
"default": "0",
@@ -495,6 +496,7 @@
"read_only": 1
},
{
"default": "1",
"fieldname": "asset_quantity",
"fieldtype": "Int",
"label": "Asset Quantity",
@@ -531,6 +533,22 @@
"label": "Capitalized In",
"options": "Asset Capitalization",
"read_only": 1
},
{
"depends_on": "eval:doc.docstatus > 0",
"fieldname": "total_asset_cost",
"fieldtype": "Currency",
"label": "Total Asset Cost",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"depends_on": "eval:doc.docstatus > 0",
"fieldname": "additional_asset_cost",
"fieldtype": "Currency",
"label": "Additional Asset Cost",
"options": "Company:company:default_currency",
"read_only": 1
}
],
"idx": 72,
@@ -564,7 +582,7 @@
"link_fieldname": "target_asset"
}
],
"modified": "2023-11-15 17:40:17.315203",
"modified": "2024-01-15 17:35:49.226603",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",
@@ -608,4 +626,4 @@
"states": [],
"title_field": "asset_name",
"track_changes": 1
}
}

View File

@@ -10,6 +10,7 @@ from frappe import _
from frappe.utils import (
add_days,
add_months,
add_years,
cint,
date_diff,
flt,
@@ -23,6 +24,7 @@ from frappe.utils import (
import erpnext
from erpnext.accounts.general_ledger import make_reverse_gl_entries
from erpnext.accounts.utils import get_fiscal_year
from erpnext.assets.doctype.asset.depreciation import (
get_depreciation_accounts,
get_disposal_account_and_cost_center,
@@ -43,10 +45,12 @@ class Asset(AccountsController):
self.validate_finance_books()
if not self.split_from:
self.prepare_depreciation_data()
self.update_shift_depr_schedule()
self.validate_gross_and_purchase_amount()
if self.get("schedules"):
self.validate_expected_value_after_useful_life()
self.total_asset_cost = self.gross_purchase_amount
self.status = self.get_status()
def on_submit(self):
@@ -59,6 +63,7 @@ class Asset(AccountsController):
def on_cancel(self):
self.validate_cancellation()
self.cancel_movement_entries()
self.cancel_capitalization()
self.delete_depreciation_entries()
self.set_status()
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
@@ -104,6 +109,13 @@ class Asset(AccountsController):
self.opening_accumulated_depreciation
)
def update_shift_depr_schedule(self):
if not any(fb.get("shift_based") for fb in self.finance_books) or self.docstatus != 0:
return
self.make_depreciation_schedule()
self.set_accumulated_depreciation()
def should_prepare_depreciation_schedule(self):
if not self.get("schedules"):
return True
@@ -238,7 +250,12 @@ class Asset(AccountsController):
frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError)
if is_cwip_accounting_enabled(self.asset_category):
if not self.is_existing_asset and not (self.purchase_receipt or self.purchase_invoice):
if (
not self.is_existing_asset
and not self.is_composite_asset
and not self.purchase_receipt
and not self.purchase_invoice
):
frappe.throw(
_("Please create purchase receipt or purchase invoice for the item {0}").format(
self.item_code
@@ -251,7 +268,7 @@ class Asset(AccountsController):
and not frappe.db.get_value("Purchase Invoice", self.purchase_invoice, "update_stock")
):
frappe.throw(
_("Update stock must be enable for the purchase invoice {0}").format(self.purchase_invoice)
_("Update stock must be enabled for the purchase invoice {0}").format(self.purchase_invoice)
)
if not self.calculate_depreciation:
@@ -318,7 +335,7 @@ class Asset(AccountsController):
self.get_depreciation_rate(d, on_validate=True), d.precision("rate_of_depreciation")
)
def make_depreciation_schedule(self, date_of_disposal, value_after_depreciation=None):
def make_depreciation_schedule(self, date_of_disposal=None, value_after_depreciation=None):
if not self.get("schedules"):
self.schedules = []
@@ -366,14 +383,24 @@ class Asset(AccountsController):
should_get_last_day = is_last_day_of_the_month(finance_book.depreciation_start_date)
depreciation_amount = 0
number_of_pending_depreciations = final_number_of_depreciations - start[finance_book.idx - 1]
yearly_opening_wdv = value_after_depreciation
current_fiscal_year_end_date = None
for n in range(start[finance_book.idx - 1], final_number_of_depreciations):
# If depreciation is already completed (for double declining balance)
if skip_row:
continue
schedule_date = add_months(
finance_book.depreciation_start_date, n * cint(finance_book.frequency_of_depreciation)
)
if not current_fiscal_year_end_date:
current_fiscal_year_end_date = get_fiscal_year(finance_book.depreciation_start_date)[2]
elif getdate(schedule_date) > getdate(current_fiscal_year_end_date):
current_fiscal_year_end_date = add_years(current_fiscal_year_end_date, 1)
yearly_opening_wdv = value_after_depreciation
if n > 0 and len(self.get("schedules")) > n - 1:
prev_depreciation_amount = self.get("schedules")[n - 1].depreciation_amount
else:
@@ -382,6 +409,7 @@ class Asset(AccountsController):
depreciation_amount = get_depreciation_amount(
self,
value_after_depreciation,
yearly_opening_wdv,
finance_book,
n,
prev_depreciation_amount,
@@ -410,13 +438,7 @@ class Asset(AccountsController):
)
if depreciation_amount > 0:
self._add_depreciation_row(
date_of_disposal,
depreciation_amount,
finance_book.depreciation_method,
finance_book.finance_book,
finance_book.idx,
)
self._add_depreciation_row(date_of_disposal, depreciation_amount, finance_book, n)
break
@@ -425,6 +447,7 @@ class Asset(AccountsController):
n == 0
and (has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata)
and not self.opening_accumulated_depreciation
and not self.flags.wdv_it_act_applied
):
from_date = add_days(
self.available_for_use_date, -1
@@ -484,7 +507,10 @@ class Asset(AccountsController):
if not depreciation_amount:
continue
value_after_depreciation -= flt(depreciation_amount, self.precision("gross_purchase_amount"))
value_after_depreciation = flt(
value_after_depreciation - flt(depreciation_amount),
self.precision("gross_purchase_amount"),
)
# Adjust depreciation amount in the last period based on the expected value after useful life
if finance_book.expected_value_after_useful_life and (
@@ -498,25 +524,27 @@ class Asset(AccountsController):
skip_row = True
if flt(depreciation_amount, self.precision("gross_purchase_amount")) > 0:
self._add_depreciation_row(
schedule_date,
depreciation_amount,
finance_book.depreciation_method,
finance_book.finance_book,
finance_book.idx,
)
self._add_depreciation_row(schedule_date, depreciation_amount, finance_book, n)
def _add_depreciation_row(self, schedule_date, depreciation_amount, finance_book, schedule_idx):
if finance_book.shift_based:
shift = (
self.schedules_before_clearing[schedule_idx].shift
if self.schedules_before_clearing and len(self.schedules_before_clearing) > schedule_idx
else frappe.get_cached_value("Asset Shift Factor", {"default": 1}, "shift_name")
)
else:
shift = None
def _add_depreciation_row(
self, schedule_date, depreciation_amount, depreciation_method, finance_book, finance_book_id
):
self.append(
"schedules",
{
"schedule_date": schedule_date,
"depreciation_amount": depreciation_amount,
"depreciation_method": depreciation_method,
"finance_book": finance_book,
"finance_book_id": finance_book_id,
"depreciation_method": finance_book.depreciation_method,
"finance_book": finance_book.finance_book,
"finance_book_id": finance_book.idx,
"shift": shift,
},
)
@@ -545,6 +573,8 @@ class Asset(AccountsController):
num_of_depreciations_completed = 0
depr_schedule = []
self.schedules_before_clearing = self.get("schedules")
for schedule in self.get("schedules"):
# to update start when there are JEs linked with all the schedule rows corresponding to an FB
if len(start) == (int(schedule.finance_book_id) - 2):
@@ -752,10 +782,12 @@ class Asset(AccountsController):
and not date_of_return
):
book = self.get("finance_books")[cint(d.finance_book_id) - 1]
depreciation_amount += flt(
value_after_depreciation - flt(book.expected_value_after_useful_life),
d.precision("depreciation_amount"),
)
if not book.shift_based:
depreciation_amount += flt(
value_after_depreciation - flt(book.expected_value_after_useful_life),
d.precision("depreciation_amount"),
)
d.depreciation_amount = depreciation_amount
accumulated_depreciation += d.depreciation_amount
@@ -816,6 +848,16 @@ class Asset(AccountsController):
movement = frappe.get_doc("Asset Movement", movement.get("name"))
movement.cancel()
def cancel_capitalization(self):
asset_capitalization = frappe.db.get_value(
"Asset Capitalization",
{"target_asset": self.name, "docstatus": 1, "entry_type": "Capitalization"},
)
if asset_capitalization:
asset_capitalization = frappe.get_doc("Asset Capitalization", asset_capitalization)
asset_capitalization.cancel()
def delete_depreciation_entries(self):
if self.calculate_depreciation:
for d in self.get("schedules"):
@@ -1217,10 +1259,12 @@ def get_item_details(item_code, asset_category, gross_purchase_amount):
"total_number_of_depreciations": d.total_number_of_depreciations,
"frequency_of_depreciation": d.frequency_of_depreciation,
"daily_prorata_based": d.daily_prorata_based,
"shift_based": d.shift_based,
"salvage_value_percentage": d.salvage_value_percentage,
"expected_value_after_useful_life": flt(gross_purchase_amount)
* flt(d.salvage_value_percentage / 100),
"depreciation_start_date": d.depreciation_start_date or nowdate(),
"rate_of_depreciation": d.rate_of_depreciation,
}
)
@@ -1334,6 +1378,8 @@ def is_cwip_accounting_enabled(asset_category):
@frappe.whitelist()
def get_asset_value_after_depreciation(asset_name, finance_book=None):
asset = frappe.get_doc("Asset", asset_name)
if not asset.calculate_depreciation:
return flt(asset.value_after_depreciation)
return asset.get_value_after_depreciation(finance_book)
@@ -1350,6 +1396,7 @@ def get_total_days(date, frequency):
def get_depreciation_amount(
asset,
depreciable_value,
yearly_opening_wdv,
fb_row,
schedule_idx=0,
prev_depreciation_amount=0,
@@ -1363,27 +1410,23 @@ def get_depreciation_amount(
asset, fb_row, schedule_idx, number_of_pending_depreciations
)
else:
rate_of_depreciation = get_updated_rate_of_depreciation_for_wdv_and_dd(
asset, depreciable_value, fb_row
)
return get_wdv_or_dd_depr_amount(
asset,
fb_row,
depreciable_value,
rate_of_depreciation,
fb_row.frequency_of_depreciation,
yearly_opening_wdv,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
)
@erpnext.allow_regional
def get_updated_rate_of_depreciation_for_wdv_and_dd(asset, depreciable_value, fb_row):
return fb_row.rate_of_depreciation
def get_straight_line_or_manual_depr_amount(
asset, row, schedule_idx, number_of_pending_depreciations
):
if row.shift_based:
return get_shift_depr_amount(asset, row, schedule_idx)
# if the Depreciation Schedule is being modified after Asset Repair due to increase in asset life and value
if asset.flags.increase_in_asset_life:
return (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / (
@@ -1478,30 +1521,88 @@ def get_straight_line_or_manual_depr_amount(
) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked)
def get_shift_depr_amount(asset, row, schedule_idx):
if asset.get("__islocal") and not asset.flags.shift_allocation:
return (
flt(asset.gross_purchase_amount)
- flt(asset.opening_accumulated_depreciation)
- flt(row.expected_value_after_useful_life)
) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked)
asset_shift_factors_map = get_asset_shift_factors_map()
shift = (
asset.schedules_before_clearing[schedule_idx].shift
if len(asset.schedules_before_clearing) > schedule_idx
else None
)
shift_factor = asset_shift_factors_map.get(shift) if shift else 0
shift_factors_sum = sum(
flt(asset_shift_factors_map.get(schedule.shift)) for schedule in asset.schedules_before_clearing
)
return (
(
flt(asset.gross_purchase_amount)
- flt(asset.opening_accumulated_depreciation)
- flt(row.expected_value_after_useful_life)
)
/ flt(shift_factors_sum)
) * shift_factor
def get_asset_shift_factors_map():
return dict(frappe.db.get_all("Asset Shift Factor", ["shift_name", "shift_factor"], as_list=True))
@erpnext.allow_regional
def get_wdv_or_dd_depr_amount(
asset,
fb_row,
depreciable_value,
rate_of_depreciation,
frequency_of_depreciation,
yearly_opening_wdv,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
):
if cint(frequency_of_depreciation) == 12:
return flt(depreciable_value) * (flt(rate_of_depreciation) / 100)
return get_default_wdv_or_dd_depr_amount(
asset,
fb_row,
depreciable_value,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
)
def get_default_wdv_or_dd_depr_amount(
asset,
fb_row,
depreciable_value,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
):
if cint(fb_row.frequency_of_depreciation) == 12:
return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100)
else:
if has_wdv_or_dd_non_yearly_pro_rata:
if schedule_idx == 0:
return flt(depreciable_value) * (flt(rate_of_depreciation) / 100)
elif schedule_idx % (12 / cint(frequency_of_depreciation)) == 1:
return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100)
elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1:
return (
flt(depreciable_value) * flt(frequency_of_depreciation) * (flt(rate_of_depreciation) / 1200)
flt(depreciable_value)
* flt(fb_row.frequency_of_depreciation)
* (flt(fb_row.rate_of_depreciation) / 1200)
)
else:
return prev_depreciation_amount
else:
if schedule_idx % (12 / cint(frequency_of_depreciation)) == 0:
if schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 0:
return (
flt(depreciable_value) * flt(frequency_of_depreciation) * (flt(rate_of_depreciation) / 1200)
flt(depreciable_value)
* flt(fb_row.frequency_of_depreciation)
* (flt(fb_row.rate_of_depreciation) / 1200)
)
else:
return prev_depreciation_amount

View File

@@ -19,6 +19,7 @@ from frappe.utils import (
from frappe.utils.data import get_link_to_form
from frappe.utils.user import get_users_with_role
import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_checks_for_pl_and_bs_accounts,
)
@@ -464,14 +465,27 @@ def restore_asset(asset_name):
def depreciate_asset(asset, date):
if not asset.calculate_depreciation:
return
asset.flags.ignore_validate_update_after_submit = True
asset.prepare_depreciation_data(date_of_disposal=date)
asset.save()
make_depreciation_entry(asset.name, date)
cancel_depreciation_entries(asset, date)
@erpnext.allow_regional
def cancel_depreciation_entries(asset, date):
pass
def reset_depreciation_schedule(asset, date):
if not asset.calculate_depreciation:
return
asset.flags.ignore_validate_update_after_submit = True
# recreate original depreciation schedule of the asset

View File

@@ -142,12 +142,7 @@ class TestAsset(AssetSetup):
("Creditors - _TC", 0.0, 100000.0),
)
gle = frappe.db.sql(
"""select account, debit, credit from `tabGL Entry`
where voucher_type='Purchase Invoice' and voucher_no = %s
order by account""",
pi.name,
)
gle = get_gl_entries("Purchase Invoice", pi.name)
self.assertSequenceEqual(gle, expected_gle)
pi.cancel()
@@ -249,12 +244,7 @@ class TestAsset(AssetSetup):
),
)
gle = frappe.db.sql(
"""select account, debit, credit from `tabGL Entry`
where voucher_type='Journal Entry' and voucher_no = %s
order by account""",
asset.journal_entry_for_scrap,
)
gle = get_gl_entries("Journal Entry", asset.journal_entry_for_scrap)
self.assertSequenceEqual(gle, expected_gle)
restore_asset(asset.name)
@@ -316,13 +306,7 @@ class TestAsset(AssetSetup):
("Debtors - _TC", 25000.0, 0.0),
)
gle = frappe.db.sql(
"""select account, debit, credit from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no = %s
order by account""",
si.name,
)
gle = get_gl_entries("Sales Invoice", si.name)
self.assertSequenceEqual(gle, expected_gle)
si.cancel()
@@ -392,13 +376,7 @@ class TestAsset(AssetSetup):
("Debtors - _TC", 40000.0, 0.0),
)
gle = frappe.db.sql(
"""select account, debit, credit from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no = %s
order by account""",
si.name,
)
gle = get_gl_entries("Sales Invoice", si.name)
self.assertSequenceEqual(gle, expected_gle)
def test_asset_with_maintenance_required_status_after_sale(self):
@@ -526,13 +504,7 @@ class TestAsset(AssetSetup):
("CWIP Account - _TC", 5250.0, 0.0),
)
pr_gle = frappe.db.sql(
"""select account, debit, credit from `tabGL Entry`
where voucher_type='Purchase Receipt' and voucher_no = %s
order by account""",
pr.name,
)
pr_gle = get_gl_entries("Purchase Receipt", pr.name)
self.assertSequenceEqual(pr_gle, expected_gle)
pi = make_invoice(pr.name)
@@ -545,13 +517,7 @@ class TestAsset(AssetSetup):
("Creditors - _TC", 0.0, 5500.0),
)
pi_gle = frappe.db.sql(
"""select account, debit, credit from `tabGL Entry`
where voucher_type='Purchase Invoice' and voucher_no = %s
order by account""",
pi.name,
)
pi_gle = get_gl_entries("Purchase Invoice", pi.name)
self.assertSequenceEqual(pi_gle, expected_gle)
asset = frappe.db.get_value("Asset", {"purchase_receipt": pr.name, "docstatus": 0}, "name")
@@ -578,13 +544,7 @@ class TestAsset(AssetSetup):
expected_gle = (("_Test Fixed Asset - _TC", 5250.0, 0.0), ("CWIP Account - _TC", 0.0, 5250.0))
gle = frappe.db.sql(
"""select account, debit, credit from `tabGL Entry`
where voucher_type='Asset' and voucher_no = %s
order by account""",
asset_doc.name,
)
gle = get_gl_entries("Asset", asset_doc.name)
self.assertSequenceEqual(gle, expected_gle)
def test_asset_cwip_toggling_cases(self):
@@ -607,10 +567,7 @@ class TestAsset(AssetSetup):
asset_doc.available_for_use_date = nowdate()
asset_doc.calculate_depreciation = 0
asset_doc.submit()
gle = frappe.db.sql(
"""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""",
asset_doc.name,
)
gle = get_gl_entries("Asset", asset_doc.name)
self.assertFalse(gle)
# case 1 -- PR with cwip disabled, Asset with cwip enabled
@@ -624,10 +581,7 @@ class TestAsset(AssetSetup):
asset_doc.available_for_use_date = nowdate()
asset_doc.calculate_depreciation = 0
asset_doc.submit()
gle = frappe.db.sql(
"""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""",
asset_doc.name,
)
gle = get_gl_entries("Asset", asset_doc.name)
self.assertFalse(gle)
# case 2 -- PR with cwip enabled, Asset with cwip disabled
@@ -640,10 +594,7 @@ class TestAsset(AssetSetup):
asset_doc.available_for_use_date = nowdate()
asset_doc.calculate_depreciation = 0
asset_doc.submit()
gle = frappe.db.sql(
"""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""",
asset_doc.name,
)
gle = get_gl_entries("Asset", asset_doc.name)
self.assertTrue(gle)
# case 3 -- PI with cwip disabled, Asset with cwip enabled
@@ -656,10 +607,7 @@ class TestAsset(AssetSetup):
asset_doc.available_for_use_date = nowdate()
asset_doc.calculate_depreciation = 0
asset_doc.submit()
gle = frappe.db.sql(
"""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""",
asset_doc.name,
)
gle = get_gl_entries("Asset", asset_doc.name)
self.assertFalse(gle)
# case 4 -- PI with cwip enabled, Asset with cwip disabled
@@ -672,10 +620,7 @@ class TestAsset(AssetSetup):
asset_doc.available_for_use_date = nowdate()
asset_doc.calculate_depreciation = 0
asset_doc.submit()
gle = frappe.db.sql(
"""select name from `tabGL Entry` where voucher_type='Asset' and voucher_no = %s""",
asset_doc.name,
)
gle = get_gl_entries("Asset", asset_doc.name)
self.assertTrue(gle)
frappe.db.set_value("Asset Category", "Computers", "enable_cwip_accounting", cwip)
@@ -900,7 +845,7 @@ class TestDepreciationMethods(AssetSetup):
["2030-12-31", 28630.14, 28630.14],
["2031-12-31", 35684.93, 64315.07],
["2032-12-31", 17842.47, 82157.54],
["2033-06-06", 5342.46, 87500.0],
["2033-06-06", 5342.47, 87500.01],
]
schedules = [
@@ -1012,7 +957,7 @@ class TestDepreciationBasics(AssetSetup):
},
)
depreciation_amount = get_depreciation_amount(asset, 100000, asset.finance_books[0])
depreciation_amount = get_depreciation_amount(asset, 100000, 100000, asset.finance_books[0])
self.assertEqual(depreciation_amount, 30000)
def test_make_depreciation_schedule(self):
@@ -1644,6 +1589,30 @@ class TestDepreciationBasics(AssetSetup):
self.assertRaises(frappe.ValidationError, jv.insert)
def test_multi_currency_asset_pr_creation(self):
pr = make_purchase_receipt(
item_code="Macbook Pro",
qty=1,
rate=100.0,
location="Test Location",
supplier="_Test Supplier USD",
currency="USD",
)
pr.submit()
self.assertTrue(get_gl_entries("Purchase Receipt", pr.name))
def get_gl_entries(doctype, docname):
gl_entry = frappe.qb.DocType("GL Entry")
return (
frappe.qb.from_(gl_entry)
.select(gl_entry.account, gl_entry.debit, gl_entry.credit)
.where((gl_entry.voucher_type == doctype) & (gl_entry.voucher_no == docname))
.orderby(gl_entry.account)
.run()
)
def create_asset_data():
if not frappe.db.exists("Asset Category", "Computers"):
@@ -1706,6 +1675,7 @@ def create_asset(**args):
"expected_value_after_useful_life": args.expected_value_after_useful_life or 0,
"depreciation_start_date": args.depreciation_start_date,
"daily_prorata_based": args.daily_prorata_based or 0,
"shift_based": args.shift_based or 0,
},
)

View File

@@ -20,10 +20,10 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
this.show_stock_ledger();
}
if (this.frm.doc.stock_items && !this.frm.doc.stock_items.length && this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") {
this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset);
this.get_target_asset_details();
}
// if (this.frm.doc.stock_items && !this.frm.doc.stock_items.length && this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") {
// this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset);
// this.get_target_asset_details();
// }
}
setup_queries() {
@@ -119,13 +119,20 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
},
callback: function (r) {
if (!r.exc && r.message) {
me.frm.clear_table("stock_items");
for (let item of r.message) {
me.frm.add_child("stock_items", item);
if(r.message[0] && r.message[0].length) {
me.frm.clear_table("stock_items");
for (let item of r.message[0]) {
me.frm.add_child("stock_items", item);
}
refresh_field("stock_items");
}
if (r.message[1] && r.message[1].length) {
me.frm.clear_table("asset_items");
for (let item of r.message[1]) {
me.frm.add_child("asset_items", item);
}
me.frm.refresh_field("asset_items");
}
refresh_field("stock_items");
me.calculate_totals();
}

View File

@@ -72,11 +72,23 @@ class AssetCapitalization(StockController):
self.update_target_asset()
def on_cancel(self):
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation")
self.ignore_linked_doctypes = (
"GL Entry",
"Stock Ledger Entry",
"Repost Item Valuation",
"Asset",
)
self.cancel_target_asset()
self.update_stock_ledger()
self.make_gl_entries()
self.restore_consumed_asset_items()
def cancel_target_asset(self):
if self.entry_type == "Capitalization" and self.target_asset:
asset_doc = frappe.get_doc("Asset", self.target_asset)
if asset_doc.docstatus == 1:
asset_doc.cancel()
def set_title(self):
self.title = self.target_asset_name or self.target_item_name or self.target_item_code
@@ -782,7 +794,6 @@ def get_consumed_asset_details(args):
out.cost_center = get_default_cost_center(
args, item_defaults, item_group_defaults, brand_defaults
)
return out
@@ -830,10 +841,27 @@ def get_items_tagged_to_wip_composite_asset(asset):
"qty",
"valuation_rate",
"amount",
"is_fixed_asset",
"parent",
]
pr_items = frappe.get_all(
"Purchase Receipt Item", filters={"wip_composite_asset": asset}, fields=fields
"Purchase Receipt Item", filters={"wip_composite_asset": asset, "docstatus": 1}, fields=fields
)
return pr_items
stock_items = []
asset_items = []
for d in pr_items:
if not d.is_fixed_asset:
stock_items.append(frappe._dict(d))
else:
asset_details = frappe.db.get_value(
"Asset",
{"item_code": d.item_code, "purchase_receipt": d.parent},
["name as asset", "asset_name"],
as_dict=1,
)
d.update(asset_details)
asset_items.append(frappe._dict(d))
return stock_items, asset_items

View File

@@ -67,12 +67,12 @@ class AssetCategory(Document):
if selected_key_type not in expected_key_types:
frappe.throw(
_(
"Row #{}: {} of {} should be {}. Please modify the account or select a different account."
"Row #{0}: {1} of {2} should be {3}. Please update the {1} or select a different account."
).format(
d.idx,
frappe.unscrub(key_to_match),
frappe.bold(selected_account),
frappe.bold(expected_key_types),
frappe.bold(" or ".join(expected_key_types)),
),
title=_("Invalid Account"),
)

View File

@@ -9,6 +9,7 @@
"depreciation_method",
"total_number_of_depreciations",
"daily_prorata_based",
"shift_based",
"column_break_5",
"frequency_of_depreciation",
"depreciation_start_date",
@@ -89,16 +90,22 @@
},
{
"default": "0",
"depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"",
"fieldname": "daily_prorata_based",
"fieldtype": "Check",
"label": "Depreciate based on daily pro-rata"
},
{
"default": "0",
"depends_on": "eval:doc.depreciation_method == \"Straight Line\"",
"fieldname": "shift_based",
"fieldtype": "Check",
"label": "Depreciate based on shifts"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2023-11-03 22:21:52.090191",
"modified": "2023-12-29 08:49:39.876439",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Finance Book",

View File

@@ -46,6 +46,10 @@ class AssetRepair(AccountsController):
self.increase_asset_value()
if self.capitalize_repair_cost:
self.asset_doc.total_asset_cost += self.repair_cost
self.asset_doc.additional_asset_cost += self.repair_cost
if self.get("stock_consumption"):
self.check_for_stock_items_and_warehouse()
self.decrease_stock_quantity()
@@ -68,6 +72,10 @@ class AssetRepair(AccountsController):
self.decrease_asset_value()
if self.capitalize_repair_cost:
self.asset_doc.total_asset_cost -= self.repair_cost
self.asset_doc.additional_asset_cost -= self.repair_cost
if self.get("stock_consumption"):
self.increase_stock_quantity()
if self.get("capitalize_repair_cost"):

View File

@@ -0,0 +1,15 @@
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Asset Shift Allocation', {
onload: function(frm) {
frm.events.make_schedules_editable(frm);
},
make_schedules_editable: function(frm) {
frm.toggle_enable("depreciation_schedule", true);
frm.fields_dict["depreciation_schedule"].grid.toggle_enable("schedule_date", false);
frm.fields_dict["depreciation_schedule"].grid.toggle_enable("depreciation_amount", false);
frm.fields_dict["depreciation_schedule"].grid.toggle_enable("shift", true);
}
});

View File

@@ -0,0 +1,115 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "naming_series:",
"creation": "2023-11-29 04:01:33.796458",
"default_view": "List",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"section_break_esaa",
"asset",
"naming_series",
"column_break_tdae",
"finance_book",
"amended_from",
"depreciation_schedule_section",
"depreciation_schedule"
],
"fields": [
{
"fieldname": "section_break_esaa",
"fieldtype": "Section Break"
},
{
"fieldname": "asset",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Asset",
"options": "Asset",
"reqd": 1
},
{
"fieldname": "naming_series",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Naming Series",
"options": "ACC-ASA-.YYYY.-",
"reqd": 1
},
{
"fieldname": "column_break_tdae",
"fieldtype": "Column Break"
},
{
"fieldname": "finance_book",
"fieldtype": "Link",
"label": "Finance Book",
"options": "Finance Book"
},
{
"depends_on": "eval:!doc.__islocal",
"fieldname": "depreciation_schedule_section",
"fieldtype": "Section Break",
"label": "Depreciation Schedule"
},
{
"fieldname": "depreciation_schedule",
"fieldtype": "Table",
"label": "Depreciation Schedule",
"options": "Depreciation Schedule"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Asset Shift Allocation",
"print_hide": 1,
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-11-29 04:06:20.586168",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Shift Allocation",
"naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"share": 1,
"submit": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -0,0 +1,247 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import add_months, cint, flt, get_last_day
from erpnext.assets.doctype.asset.asset import get_asset_shift_factors_map
from erpnext.assets.doctype.asset.depreciation import is_last_day_of_the_month
class AssetShiftAllocation(Document):
def after_insert(self):
self.fetch_and_set_depr_schedule()
def validate(self):
self.asset_doc = frappe.get_doc("Asset", self.asset)
self.validate_invalid_shift_change()
self.update_depr_schedule()
def on_submit(self):
self.update_asset_schedule()
def fetch_and_set_depr_schedule(self):
if len(self.asset_doc.finance_books) != 1:
frappe.throw(_("Only assets with one finance book allowed in v14."))
if not any(fb.get("shift_based") for fb in self.asset_doc.finance_books):
frappe.throw(_("Asset {0} is not using shift based depreciation").format(self.asset))
for schedule in self.asset_doc.get("schedules"):
self.append(
"depreciation_schedule",
{
"schedule_date": schedule.schedule_date,
"depreciation_amount": schedule.depreciation_amount,
"accumulated_depreciation_amount": schedule.accumulated_depreciation_amount,
"journal_entry": schedule.journal_entry,
"shift": schedule.shift,
"depreciation_method": self.asset_doc.finance_books[0].depreciation_method,
"finance_book": self.asset_doc.finance_books[0].finance_book,
"finance_book_id": self.asset_doc.finance_books[0].idx,
},
)
self.flags.ignore_validate = True
self.save()
def validate_invalid_shift_change(self):
if not self.get("depreciation_schedule") or self.docstatus == 1:
return
for i, sch in enumerate(self.depreciation_schedule):
if sch.journal_entry and self.asset_doc.schedules[i].shift != sch.shift:
frappe.throw(
_(
"Row {0}: Shift cannot be changed since the depreciation has already been processed"
).format(i)
)
def update_depr_schedule(self):
if not self.get("depreciation_schedule") or self.docstatus == 1:
return
self.allocate_shift_diff_in_depr_schedule()
temp_asset_doc = frappe.copy_doc(self.asset_doc)
temp_asset_doc.flags.shift_allocation = True
temp_asset_doc.schedules = []
for schedule in self.depreciation_schedule:
temp_asset_doc.append(
"schedules",
{
"schedule_date": schedule.schedule_date,
"depreciation_amount": schedule.depreciation_amount,
"accumulated_depreciation_amount": schedule.accumulated_depreciation_amount,
"journal_entry": schedule.journal_entry,
"shift": schedule.shift,
"depreciation_method": self.asset_doc.finance_books[0].depreciation_method,
"finance_book": self.asset_doc.finance_books[0].finance_book,
"finance_book_id": self.asset_doc.finance_books[0].idx,
},
)
temp_asset_doc.prepare_depreciation_data()
self.depreciation_schedule = []
for schedule in temp_asset_doc.get("schedules"):
self.append(
"depreciation_schedule",
{
"schedule_date": schedule.schedule_date,
"depreciation_amount": schedule.depreciation_amount,
"accumulated_depreciation_amount": schedule.accumulated_depreciation_amount,
"journal_entry": schedule.journal_entry,
"shift": schedule.shift,
"depreciation_method": self.asset_doc.finance_books[0].depreciation_method,
"finance_book": self.asset_doc.finance_books[0].finance_book,
"finance_book_id": self.asset_doc.finance_books[0].idx,
},
)
def allocate_shift_diff_in_depr_schedule(self):
asset_shift_factors_map = get_asset_shift_factors_map()
reverse_asset_shift_factors_map = {
asset_shift_factors_map[k]: k for k in asset_shift_factors_map
}
original_shift_factors_sum = sum(
flt(asset_shift_factors_map.get(schedule.shift)) for schedule in self.asset_doc.schedules
)
new_shift_factors_sum = sum(
flt(asset_shift_factors_map.get(schedule.shift)) for schedule in self.depreciation_schedule
)
diff = new_shift_factors_sum - original_shift_factors_sum
if diff > 0:
for i, schedule in reversed(list(enumerate(self.depreciation_schedule))):
if diff <= 0:
break
shift_factor = flt(asset_shift_factors_map.get(schedule.shift))
if shift_factor <= diff:
self.depreciation_schedule.pop()
diff -= shift_factor
else:
try:
self.depreciation_schedule[i].shift = reverse_asset_shift_factors_map.get(
shift_factor - diff
)
diff = 0
except Exception:
frappe.throw(_("Could not auto update shifts. Shift with shift factor {0} needed.")).format(
shift_factor - diff
)
elif diff < 0:
shift_factors = list(asset_shift_factors_map.values())
desc_shift_factors = sorted(shift_factors, reverse=True)
depr_schedule_len_diff = self.asset_doc.total_number_of_depreciations - len(
self.depreciation_schedule
)
subsets_result = []
if depr_schedule_len_diff > 0:
num_rows_to_add = depr_schedule_len_diff
while not subsets_result and num_rows_to_add > 0:
find_subsets_with_sum(shift_factors, num_rows_to_add, abs(diff), [], subsets_result)
if subsets_result:
break
num_rows_to_add -= 1
if subsets_result:
for i in range(num_rows_to_add):
schedule_date = add_months(
self.depreciation_schedule[-1].schedule_date,
cint(self.asset_doc.frequency_of_depreciation),
)
if is_last_day_of_the_month(self.depreciation_schedule[-1].schedule_date):
schedule_date = get_last_day(schedule_date)
self.append(
"depreciation_schedule",
{
"schedule_date": schedule_date,
"shift": reverse_asset_shift_factors_map.get(subsets_result[0][i]),
"depreciation_method": self.asset_doc.finance_books[0].depreciation_method,
"finance_book": self.asset_doc.finance_books[0].finance_book,
"finance_book_id": self.asset_doc.finance_books[0].idx,
},
)
if depr_schedule_len_diff <= 0 or not subsets_result:
for i, schedule in reversed(list(enumerate(self.depreciation_schedule))):
diff = abs(diff)
if diff <= 0:
break
shift_factor = flt(asset_shift_factors_map.get(schedule.shift))
if shift_factor <= diff:
for sf in desc_shift_factors:
if sf - shift_factor <= diff:
self.depreciation_schedule[i].shift = reverse_asset_shift_factors_map.get(sf)
diff -= sf - shift_factor
break
else:
try:
self.depreciation_schedule[i].shift = reverse_asset_shift_factors_map.get(
shift_factor + diff
)
diff = 0
except Exception:
frappe.throw(_("Could not auto update shifts. Shift with shift factor {0} needed.")).format(
shift_factor + diff
)
def update_asset_schedule(self):
self.asset_doc.flags.shift_allocation = True
self.asset_doc.schedules = []
for schedule in self.depreciation_schedule:
self.asset_doc.append(
"schedules",
{
"schedule_date": schedule.schedule_date,
"depreciation_amount": schedule.depreciation_amount,
"accumulated_depreciation_amount": schedule.accumulated_depreciation_amount,
"journal_entry": schedule.journal_entry,
"shift": schedule.shift,
"depreciation_method": self.asset_doc.finance_books[0].depreciation_method,
"finance_book": self.asset_doc.finance_books[0].finance_book,
"finance_book_id": self.asset_doc.finance_books[0].idx,
},
)
self.asset_doc.flags.ignore_validate_update_after_submit = True
self.asset_doc.prepare_depreciation_data()
self.asset_doc.save()
def find_subsets_with_sum(numbers, k, target_sum, current_subset, result):
if k == 0 and target_sum == 0:
result.append(current_subset.copy())
return
if k <= 0 or target_sum <= 0 or not numbers:
return
# Include the current number in the subset
find_subsets_with_sum(
numbers, k - 1, target_sum - numbers[0], current_subset + [numbers[0]], result
)
# Exclude the current number from the subset
find_subsets_with_sum(numbers[1:], k, target_sum, current_subset, result)

View File

@@ -0,0 +1,112 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import cstr
from erpnext.assets.doctype.asset.test_asset import create_asset
class TestAssetShiftAllocation(FrappeTestCase):
@classmethod
def setUpClass(cls):
create_asset_shift_factors()
@classmethod
def tearDownClass(cls):
frappe.db.rollback()
def test_asset_shift_allocation(self):
asset = create_asset(
calculate_depreciation=1,
available_for_use_date="2023-01-01",
purchase_date="2023-01-01",
gross_purchase_amount=120000,
depreciation_start_date="2023-01-31",
total_number_of_depreciations=12,
frequency_of_depreciation=1,
shift_based=1,
submit=1,
)
expected_schedules = [
["2023-01-31", 10000.0, 10000.0, "Single"],
["2023-02-28", 10000.0, 20000.0, "Single"],
["2023-03-31", 10000.0, 30000.0, "Single"],
["2023-04-30", 10000.0, 40000.0, "Single"],
["2023-05-31", 10000.0, 50000.0, "Single"],
["2023-06-30", 10000.0, 60000.0, "Single"],
["2023-07-31", 10000.0, 70000.0, "Single"],
["2023-08-31", 10000.0, 80000.0, "Single"],
["2023-09-30", 10000.0, 90000.0, "Single"],
["2023-10-31", 10000.0, 100000.0, "Single"],
["2023-11-30", 10000.0, 110000.0, "Single"],
["2023-12-31", 10000.0, 120000.0, "Single"],
]
schedules = [
[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount, d.shift]
for d in asset.get("schedules")
]
self.assertEqual(schedules, expected_schedules)
asset_shift_allocation = frappe.get_doc(
{"doctype": "Asset Shift Allocation", "asset": asset.name}
).insert()
schedules = [
[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount, d.shift]
for d in asset_shift_allocation.get("depreciation_schedule")
]
self.assertEqual(schedules, expected_schedules)
asset_shift_allocation = frappe.get_doc("Asset Shift Allocation", asset_shift_allocation.name)
asset_shift_allocation.depreciation_schedule[4].shift = "Triple"
asset_shift_allocation.save()
schedules = [
[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount, d.shift]
for d in asset_shift_allocation.get("depreciation_schedule")
]
expected_schedules = [
["2023-01-31", 10000.0, 10000.0, "Single"],
["2023-02-28", 10000.0, 20000.0, "Single"],
["2023-03-31", 10000.0, 30000.0, "Single"],
["2023-04-30", 10000.0, 40000.0, "Single"],
["2023-05-31", 20000.0, 60000.0, "Triple"],
["2023-06-30", 10000.0, 70000.0, "Single"],
["2023-07-31", 10000.0, 80000.0, "Single"],
["2023-08-31", 10000.0, 90000.0, "Single"],
["2023-09-30", 10000.0, 100000.0, "Single"],
["2023-10-31", 10000.0, 110000.0, "Single"],
["2023-11-30", 10000.0, 120000.0, "Single"],
]
self.assertEqual(schedules, expected_schedules)
asset_shift_allocation.submit()
asset.reload()
schedules = [
[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount, d.shift]
for d in asset.get("schedules")
]
self.assertEqual(schedules, expected_schedules)
def create_asset_shift_factors():
shifts = [
{"doctype": "Asset Shift Factor", "shift_name": "Half", "shift_factor": 0.5, "default": 0},
{"doctype": "Asset Shift Factor", "shift_name": "Single", "shift_factor": 1, "default": 1},
{"doctype": "Asset Shift Factor", "shift_name": "Double", "shift_factor": 1.5, "default": 0},
{"doctype": "Asset Shift Factor", "shift_name": "Triple", "shift_factor": 2, "default": 0},
]
for s in shifts:
frappe.get_doc(s).insert()

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