Compare commits

...

293 Commits

Author SHA1 Message Date
Frappe PR Bot
da3eddeb26 chore(release): Bumped to Version 15.31.3
## [15.31.3](https://github.com/frappe/erpnext/compare/v15.31.2...v15.31.3) (2024-07-31)

### Bug Fixes

* Adjust initial month's depreciation to end of depreciation period ([9d2ef4d](9d2ef4d3e8))
* builtins.KeyError: ('ABC', 'Store - CP') (backport [#42505](https://github.com/frappe/erpnext/issues/42505)) ([#42509](https://github.com/frappe/erpnext/issues/42509)) ([f25b38c](f25b38caf5))
* consider payment entries for checking if tds is deducted ([183ac41](183ac41550))
* dynamic condition in the pricing rule not working (backport [#42467](https://github.com/frappe/erpnext/issues/42467)) ([#42495](https://github.com/frappe/erpnext/issues/42495)) ([ac2ef21](ac2ef21896))
* field_type is small text for v15 ([9e99eda](9e99eda3c3))
* fields alteration related to subcontracting ([80d4dc2](80d4dc2016))
* Fields Modification for Subcontracting DocTypes ([#42383](https://github.com/frappe/erpnext/issues/42383)) ([422824b](422824b9e7))
* ignore duplicates while creating default templates ([aea8271](aea8271f7e))
* incorrect cost_center on AR/AP report ([0c2e948](0c2e9480cb))
* incorrect current qty for the batch in stock reco (backport [#42434](https://github.com/frappe/erpnext/issues/42434)) ([#42459](https://github.com/frappe/erpnext/issues/42459)) ([298a569](298a5699f1))
* keyerror posting_time (backport [#42452](https://github.com/frappe/erpnext/issues/42452)) ([#42460](https://github.com/frappe/erpnext/issues/42460)) ([2d2140a](2d2140aad0))
* not able to save BOM Creator ([#42540](https://github.com/frappe/erpnext/issues/42540)) ([61280e6](61280e6072))
* parenttype in item wise purchase and sales register ([322fbe9](322fbe92ee))
* performance issue for the report Purchase Order Analysis report (backport [#42503](https://github.com/frappe/erpnext/issues/42503)) ([#42507](https://github.com/frappe/erpnext/issues/42507)) ([edf1fcb](edf1fcb742))
* price_list_currency not found error (backport [#42534](https://github.com/frappe/erpnext/issues/42534)) ([#42539](https://github.com/frappe/erpnext/issues/42539)) ([5fa185d](5fa185d480))
* purchase return from rejected warehouse (backport [#42531](https://github.com/frappe/erpnext/issues/42531)) ([#42535](https://github.com/frappe/erpnext/issues/42535)) ([b7d70ac](b7d70ac928))
* set pos data if not return doc ([25fe08e](25fe08eb74))
* **tests:** added tests for usecase ([1390c86](1390c86fc4))
* warehouse filter in Product Bundle Balance (backport [#42532](https://github.com/frappe/erpnext/issues/42532)) ([#42537](https://github.com/frappe/erpnext/issues/42537)) ([826577c](826577c88f))
* Warranty Expiry Date not set in the serial number (backport [#42513](https://github.com/frappe/erpnext/issues/42513)) ([#42515](https://github.com/frappe/erpnext/issues/42515)) ([fc0db19](fc0db1941a))

### Performance Improvements

* huge number of serial no creation (backport [#42522](https://github.com/frappe/erpnext/issues/42522)) ([#42544](https://github.com/frappe/erpnext/issues/42544)) ([6840f6c](6840f6cb26))
2024-07-31 06:03:49 +00:00
ruthra kumar
43dadc763c Merge pull request #42541 from frappe/version-15-hotfix
chore: release v15
2024-07-31 11:32:35 +05:30
Khushi Rawat
64c185f097 Merge pull request #42546 from frappe/mergify/bp/version-15-hotfix/pr-42525
fix: Adjust initial month's depreciation to end of depreciation period (backport #42525)
2024-07-30 19:24:32 +05:30
Khushi Rawat
1390c86fc4 fix(tests): added tests for usecase
(cherry picked from commit f0768010d9)
2024-07-30 13:35:18 +00:00
Khushi Rawat
9d2ef4d3e8 fix: Adjust initial month's depreciation to end of depreciation period
(cherry picked from commit cbb749a3a5)
2024-07-30 13:35:17 +00:00
mergify[bot]
6840f6cb26 perf: huge number of serial no creation (backport #42522) (#42544)
perf: huge number of serial no creation (#42522)

(cherry picked from commit 1c7f7c8d1a)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-30 17:01:43 +05:30
rohitwaghchaure
61280e6072 fix: not able to save BOM Creator (#42540) 2024-07-30 15:13:19 +05:30
mergify[bot]
b7d70ac928 fix: purchase return from rejected warehouse (backport #42531) (#42535)
fix: purchase return from rejected warehouse (#42531)

(cherry picked from commit c5d68333c9)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-30 14:53:42 +05:30
mergify[bot]
826577c88f fix: warehouse filter in Product Bundle Balance (backport #42532) (#42537)
fix: warehouse filter in Product Bundle Balance (#42532)

(cherry picked from commit 0ecfa709d8)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-30 14:53:03 +05:30
mergify[bot]
5fa185d480 fix: price_list_currency not found error (backport #42534) (#42539)
fix: price_list_currency not found error (#42534)

(cherry picked from commit 23fed831a0)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-30 14:52:23 +05:30
ruthra kumar
75271ca1a0 Merge pull request #42533 from frappe/mergify/bp/version-15-hotfix/pr-42476
refactor: checkbox to control Payment Request creation (backport #42476)
2024-07-30 14:04:48 +05:30
ruthra kumar
f03e58f5f6 chore: resolve conflict 2024-07-30 13:39:14 +05:30
ruthra kumar
596110dd65 refactor: checkbox to control Payment Request creation
(cherry picked from commit ce81fd9ba6)

# Conflicts:
#	erpnext/accounts/doctype/payment_request/payment_request.py
2024-07-30 08:07:04 +00:00
ruthra kumar
76018f5b9c Merge pull request #42530 from frappe/mergify/bp/version-15-hotfix/pr-42528
chore: button name should reflect what it creates (backport #42528)
2024-07-30 12:12:28 +05:30
ruthra kumar
1019f6d158 chore: button name should reflect what it creates
(cherry picked from commit 0b6e7f83cd)
2024-07-30 06:31:20 +00:00
mergify[bot]
fc0db1941a fix: Warranty Expiry Date not set in the serial number (backport #42513) (#42515)
fix: Warranty Expiry Date not set in the serial number (#42513)

* fix: Warranty Expiry Date not set in the serial number

* chore: fix linters issue

(cherry picked from commit 8eff168d76)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-29 15:44:01 +05:30
mergify[bot]
f25b38caf5 fix: builtins.KeyError: ('ABC', 'Store - CP') (backport #42505) (#42509)
fix: builtins.KeyError: ('ABC', 'Store - CP') (#42505)

(cherry picked from commit 25dac1f18e)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-29 10:21:11 +05:30
mergify[bot]
edf1fcb742 fix: performance issue for the report Purchase Order Analysis report (backport #42503) (#42507)
fix: performance issue for the report Purchase Order Analysis report (#42503)

(cherry picked from commit cb522f8f22)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-29 09:50:02 +05:30
Frappe PR Bot
4b66fcad64 chore(release): Bumped to Version 15.31.2
## [15.31.2](https://github.com/frappe/erpnext/compare/v15.31.1...v15.31.2) (2024-07-27)

### Bug Fixes

* dynamic condition in the pricing rule not working (backport [#42467](https://github.com/frappe/erpnext/issues/42467)) (backport [#42495](https://github.com/frappe/erpnext/issues/42495)) ([#42496](https://github.com/frappe/erpnext/issues/42496)) ([334c4d0](334c4d0676))
2024-07-27 05:38:30 +00:00
mergify[bot]
334c4d0676 fix: dynamic condition in the pricing rule not working (backport #42467) (backport #42495) (#42496)
fix: dynamic condition in the pricing rule not working (backport #42467) (#42495)

fix: dynamic condition in the pricing rule not working (#42467)

(cherry picked from commit 0e817f42ef)

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-07-27 11:07:20 +05:30
mergify[bot]
ac2ef21896 fix: dynamic condition in the pricing rule not working (backport #42467) (#42495)
fix: dynamic condition in the pricing rule not working (#42467)

(cherry picked from commit 0e817f42ef)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-27 10:41:07 +05:30
ruthra kumar
b529dc7dd1 Merge pull request #42492 from frappe/mergify/bp/version-15-hotfix/pr-42477
fix: incorrect cost_center on AR/AP report (backport #42477)
2024-07-26 20:10:30 +05:30
ruthra kumar
4b24f8bc04 test: invoice cost center reported in AR/AP report
(cherry picked from commit 9a0894fd65)
2024-07-26 14:14:32 +00:00
ruthra kumar
0c2e9480cb fix: incorrect cost_center on AR/AP report
(cherry picked from commit 3e19041fa3)
2024-07-26 14:14:31 +00:00
Smit Vora
901f83edd5 Merge pull request #42487 from frappe/mergify/bp/version-15-hotfix/pr-42470
fix: Fields Alteration Related to Subcontracting (backport #42470)
2024-07-26 18:23:11 +05:30
Ninad1306
80d4dc2016 fix: fields alteration related to subcontracting
(cherry picked from commit 77590e6077)
2024-07-26 10:56:15 +00:00
Smit Vora
3a279db06b Merge pull request #42468 from frappe/mergify/bp/version-15-hotfix/pr-42383
fix: Fields Modification for Subcontracting DocTypes (backport #42383)
2024-07-26 16:24:10 +05:30
Smit Vora
3053254db7 Merge pull request #42484 from frappe/mergify/bp/version-15-hotfix/pr-42305
fix: consider payment entries for checking if tds is deducted (backport #42305)
2024-07-26 15:17:18 +05:30
Smit Vora
d135193f6c Merge pull request #42482 from frappe/mergify/bp/version-15-hotfix/pr-42444
fix: parenttype in purchase and sales item query (backport #42444)
2024-07-26 15:16:54 +05:30
ljain112
183ac41550 fix: consider payment entries for checking if tds is deducted
(cherry picked from commit 40b59de4cd)
2024-07-26 09:11:50 +00:00
ljain112
322fbe92ee fix: parenttype in item wise purchase and sales register
(cherry picked from commit 35981b8730)
2024-07-26 08:56:43 +00:00
Smit Vora
367b7eeeba Merge pull request #42471 from Ninad1306/backport_changes
fix: Field type is small text for v15
2024-07-26 14:04:39 +05:30
ruthra kumar
dd8fff6d43 Merge pull request #42474 from frappe/mergify/bp/version-15-hotfix/pr-42472
refactor: index on Purchase Invoice 'release_date' (backport #42472)
2024-07-25 21:51:23 +05:30
ruthra kumar
3e3bdf7491 refactor: index on Purchase Invoice 'release_date'
(cherry picked from commit 764dd12b10)
2024-07-25 15:57:59 +00:00
Ninad1306
9e99eda3c3 fix: field_type is small text for v15 2024-07-25 18:39:20 +05:30
Smit Vora
05b9432f6d Merge pull request #42402 from frappe/mergify/bp/version-15-hotfix/pr-42386
fix: set pos data if not return doc (backport #42386)
2024-07-25 11:40:49 +05:30
Ninad Parikh
422824b9e7 fix: Fields Modification for Subcontracting DocTypes (#42383)
* fix: fields renaming and reordering for enhanced user experience

* fix: dashboard data for stock entry

(cherry picked from commit 302339998f)

# Conflicts:
#	erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json
2024-07-25 04:49:14 +00:00
mergify[bot]
2d2140aad0 fix: keyerror posting_time (backport #42452) (#42460)
fix: keyerror posting_time (#42452)

fix: keyerror: posting_time
(cherry picked from commit 06e2d7265c)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-25 06:48:10 +05:30
ruthra kumar
a24fe951ed Merge pull request #42465 from frappe/mergify/bp/version-15-hotfix/pr-42462
refactor: provision for re-evaluating Exchange Rates in monthly frequency (backport #42462)
2024-07-24 22:11:29 +05:30
Frappe PR Bot
2de69e2b12 chore(release): Bumped to Version 15.31.1
## [15.31.1](https://github.com/frappe/erpnext/compare/v15.31.0...v15.31.1) (2024-07-24)

### Bug Fixes

* incorrect current qty for the batch in stock reco (backport [#42434](https://github.com/frappe/erpnext/issues/42434)) (backport [#42459](https://github.com/frappe/erpnext/issues/42459)) ([#42463](https://github.com/frappe/erpnext/issues/42463)) ([4bae419](4bae4194ab))
2024-07-24 14:00:57 +00:00
mergify[bot]
4bae4194ab fix: incorrect current qty for the batch in stock reco (backport #42434) (backport #42459) (#42463)
fix: incorrect current qty for the batch in stock reco (backport #42434) (#42459)

fix: incorrect current qty for the batch in stock reco (#42434)

(cherry picked from commit 9cd3374101)

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-07-24 19:29:40 +05:30
ruthra kumar
7b3a78e04e refactor: hooks for monthly re-evaluation jobs
(cherry picked from commit fc4e5f165c)
2024-07-24 13:15:18 +00:00
ruthra kumar
219310e817 refactor: provision for monthly re-evaluation
(cherry picked from commit ce2b9e0f1a)
2024-07-24 13:15:18 +00:00
mergify[bot]
298a5699f1 fix: incorrect current qty for the batch in stock reco (backport #42434) (#42459)
fix: incorrect current qty for the batch in stock reco (#42434)

(cherry picked from commit 9cd3374101)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-24 18:37:13 +05:30
ruthra kumar
bf34c94483 Merge pull request #42456 from frappe/mergify/bp/version-15-hotfix/pr-42390
refactor: cleaning up stale code related to reposting (backport #42390)
2024-07-24 17:34:53 +05:30
Smit Vora
a7d8202b4e Merge pull request #42440 from frappe/mergify/bp/version-15-hotfix/pr-42377
fix: ignore duplicates while creating default templates (backport #42377)
2024-07-24 14:25:15 +05:30
ruthra kumar
2ae94b2af2 chore: resolve conflicts 2024-07-24 13:01:51 +05:30
ruthra kumar
e607f3c78d refactor(test): remove assert on 'repost_required'
(cherry picked from commit e71cb4eab7)
2024-07-24 07:29:12 +00:00
ruthra kumar
d69361b1c9 refactor: remove attribute check on 'repost_required'
(cherry picked from commit 07fc952a43)
2024-07-24 07:29:11 +00:00
ruthra kumar
949d7f4b53 refactor: repost without checking on flag
(cherry picked from commit 09f429ffba)
2024-07-24 07:29:11 +00:00
ruthra kumar
01dfea3ffa chore: remove stale UI code related to repost
(cherry picked from commit fe46e1d089)
2024-07-24 07:29:11 +00:00
ruthra kumar
8b9860902c chore: remove 'repost_required' from Journal Entry
(cherry picked from commit e81373bb6a)

# Conflicts:
#	erpnext/accounts/doctype/journal_entry/journal_entry.json
2024-07-24 07:29:11 +00:00
ruthra kumar
3d8eac9b5a chore: remove 'repost_required' from purchase invoice
(cherry picked from commit a467888a67)
2024-07-24 07:29:11 +00:00
ruthra kumar
a8fe0e89a8 chore: remove stale 'repost_required' flag from sales invoice
(cherry picked from commit 06c5334f2a)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.json
2024-07-24 07:29:11 +00:00
ruthra kumar
9b52d89e03 chore: remove stale code from sales invoice
(cherry picked from commit f3fda9ce98)
2024-07-24 07:29:11 +00:00
Frappe PR Bot
e28c1e9c4b chore(release): Bumped to Version 15.31.0
# [15.31.0](https://github.com/frappe/erpnext/compare/v15.30.0...v15.31.0) (2024-07-24)

### Bug Fixes

* Consider adding warranty period to serial nos (backport [#42051](https://github.com/frappe/erpnext/issues/42051)) ([#42182](https://github.com/frappe/erpnext/issues/42182)) ([8da28dc](8da28dcfb2))
* correct validation for depreciation posting date ([ffacf42](ffacf4222b))
* Don't allow negative amount on Payment Request [#41905](https://github.com/frappe/erpnext/issues/41905) ([aee2cc2](aee2cc2e03))
* missing cr/dr notes on payment reconciliation ([0a41ccd](0a41ccda99))
* not able to save QC (backport [#42371](https://github.com/frappe/erpnext/issues/42371)) ([#42373](https://github.com/frappe/erpnext/issues/42373)) ([18500b8](18500b8e3a))
* provide initial value for `.reduce()` call ([72bc539](72bc539ffd))
* Purchase Order Analysis Report Data (backport [#42387](https://github.com/frappe/erpnext/issues/42387)) ([#42394](https://github.com/frappe/erpnext/issues/42394)) ([709be13](709be13e82))
* qty in the 'Serial No Ledger' report (backport [#42429](https://github.com/frappe/erpnext/issues/42429)) ([#42433](https://github.com/frappe/erpnext/issues/42433)) ([926fd41](926fd41a2b))
* remove proprietorship and update it with individual ([527781a](527781a588))
* rounding issue causing incorrect quantity in SE (backport [#42380](https://github.com/frappe/erpnext/issues/42380)) ([#42395](https://github.com/frappe/erpnext/issues/42395)) ([54791e9](54791e938b))
* serial and batch bundle for POS Invoice (backport [#41491](https://github.com/frappe/erpnext/issues/41491)) ([#42396](https://github.com/frappe/erpnext/issues/42396)) ([555be2b](555be2be11))
* set filter to show only submitted asset ([29fc975](29fc975fb8))
* Show the rows in AR/AP report where outstanding equals to 0.01 ([886256c](886256c86b))

### Features

* add make_regional_gl_entries override for Sales Invoice ([#42399](https://github.com/frappe/erpnext/issues/42399)) ([22b17de](22b17de2b4))
2024-07-24 07:28:24 +00:00
ruthra kumar
1e16a987dd Merge pull request #42449 from frappe/version-15-hotfix
chore: release v15
2024-07-24 12:57:10 +05:30
Khushi Rawat
c045c9a6dd Merge pull request #42450 from frappe/mergify/bp/version-15-hotfix/pr-42372
fix: correct validation for depreciation posting date (backport #42372)
2024-07-24 12:10:00 +05:30
Raffael Meyer
8141c6504e Merge pull request #42437 from frappe/mergify/bp/version-15-hotfix/pr-42419
fix: provide initial value for `.reduce()` call (backport #42419)
2024-07-23 13:06:37 +02:00
Khushi Rawat
ffacf4222b fix: correct validation for depreciation posting date
(cherry picked from commit da4ed90a3e)
2024-07-23 09:54:44 +00:00
Smit Vora
aea8271f7e fix: ignore duplicates while creating default templates
(cherry picked from commit cf55c2ab3d)
2024-07-23 04:56:36 +00:00
barredterra
72bc539ffd fix: provide initial value for .reduce() call
Fixes the error "TypeError: Reduce of empty array with no initial value" (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Reduce_of_empty_array_with_no_initial_value#invalid_cases)

(cherry picked from commit 65f80abf2f)
2024-07-22 18:42:13 +00:00
mergify[bot]
926fd41a2b fix: qty in the 'Serial No Ledger' report (backport #42429) (#42433)
fix: qty in the 'Serial No Ledger' report (#42429)

(cherry picked from commit be2648245b)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-22 18:06:56 +05:30
ruthra kumar
f2bc064da2 Merge pull request #42409 from frappe/mergify/bp/version-15-hotfix/pr-42407
test: basic test case for item-wise purchase register (backport #42407)
2024-07-21 20:08:27 +05:30
mergify[bot]
555be2be11 fix: serial and batch bundle for POS Invoice (backport #41491) (#42396)
* fix: serial and batch bundle for POS Invoice (#41491)

(cherry picked from commit e5dfc5e545)

# Conflicts:
#	erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-21 00:00:14 +05:30
mergify[bot]
8da28dcfb2 fix: Consider adding warranty period to serial nos (backport #42051) (#42182)
fix: Consider adding warranty period to serial nos (#42051)

(cherry picked from commit 55e2b876a2)

Co-authored-by: Syed Mujeer Hashmi <mujeerhashmi@4csolutions.in>
2024-07-20 11:12:17 +05:30
Smit Vora
5741f32080 Merge pull request #42420 from frappe/mergify/bp/version-15-hotfix/pr-42399
feat: add make_regional_gl_entries override for Sales Invoice (backport #42399)
2024-07-20 09:34:39 +05:30
HENRY Florian
22b17de2b4 feat: add make_regional_gl_entries override for Sales Invoice (#42399)
* feat: add make_regional_gl_entries override for Sales Invoice and Stock Controler

* chore: remove regionnal override for stock management

(cherry picked from commit 1bbe020cfd)
2024-07-20 02:56:01 +00:00
Khushi Rawat
96062dec10 Merge pull request #42413 from frappe/mergify/bp/version-15-hotfix/pr-42412
fix: set filter to show only submitted asset (backport #42412)
2024-07-19 19:24:24 +05:30
Khushi Rawat
29fc975fb8 fix: set filter to show only submitted asset
(cherry picked from commit 69b8e10e20)
2024-07-19 13:47:45 +00:00
ruthra kumar
0052ca9173 test: basic test case for item-wise purchase register
(cherry picked from commit c3c5d3f615)
2024-07-19 11:21:08 +00:00
mergify[bot]
54791e938b fix: rounding issue causing incorrect quantity in SE (backport #42380) (#42395)
fix: rounding issue causing incorrect quantity in SE (#42380)

(cherry picked from commit e1b9b432c3)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-19 14:49:56 +05:30
mergify[bot]
709be13e82 fix: Purchase Order Analysis Report Data (backport #42387) (#42394)
fix: Purchase Order Analysis Report Data (#42387)

(cherry picked from commit 67bf9bec47)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-19 14:49:39 +05:30
ruthra kumar
7893a957cb Merge pull request #42403 from frappe/mergify/bp/version-15-hotfix/pr-41959
fix: Don't allow negative amount on Payment Request #41905 (backport #41959)
2024-07-19 10:54:46 +05:30
ruthra kumar
a9146efc17 chore: resolve conflict 2024-07-19 10:36:42 +05:30
hiteshprajapati
aee2cc2e03 fix: Don't allow negative amount on Payment Request #41905
(cherry picked from commit b823aa66bf)

# Conflicts:
#	erpnext/accounts/doctype/payment_request/payment_request.json
2024-07-19 05:04:34 +00:00
ljain112
25fe08eb74 fix: set pos data if not return doc
(cherry picked from commit 65d672da65)
2024-07-19 04:56:27 +00:00
ruthra kumar
307dcea097 Merge pull request #42385 from frappe/mergify/bp/version-15-hotfix/pr-42374
fix: Show the rows in AR/AP report where outstanding equals to 0.01 (backport #42374)
2024-07-18 14:39:27 +05:30
ruthra kumar
0dae0a05d4 test: AR/AP report on miniscule outstanding
(cherry picked from commit bb9e42cce2)
2024-07-18 08:15:39 +00:00
Nabin Hait
886256c86b fix: Show the rows in AR/AP report where outstanding equals to 0.01
(cherry picked from commit e1dedc5402)
2024-07-18 08:15:39 +00:00
ruthra kumar
372a7e905c Merge pull request #42381 from frappe/mergify/bp/version-15-hotfix/pr-42369
fix: missing cr/dr notes on payment reconciliation (backport #42369)
2024-07-18 12:58:42 +05:30
ruthra kumar
3f820734b6 test: payment filter should not affect dr/cr notes
(cherry picked from commit 2d686c06ea)
2024-07-18 07:12:44 +00:00
ruthra kumar
0a41ccda99 fix: missing cr/dr notes on payment reconciliation
(cherry picked from commit a30af68e9e)
2024-07-18 07:12:44 +00:00
mergify[bot]
18500b8e3a fix: not able to save QC (backport #42371) (#42373)
fix: not able to save QC (#42371)

(cherry picked from commit 62fb495a65)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-17 21:36:47 +05:30
Smit Vora
bebd70d752 Merge pull request #42365 from frappe/mergify/bp/version-15-hotfix/pr-42307
fix: remove redundant proprietorship field from customer type and supplier type (backport #42307)
2024-07-17 18:55:22 +05:30
Sanket322
527781a588 fix: remove proprietorship and update it with individual
(cherry picked from commit c8ad6d1b2c)
2024-07-17 08:32:17 +00:00
Frappe PR Bot
4b8b3ee46a chore(release): Bumped to Version 15.30.0
# [15.30.0](https://github.com/frappe/erpnext/compare/v15.29.4...v15.30.0) (2024-07-17)

### Bug Fixes

* address and contact filters for SCO and SCR (backport [#42310](https://github.com/frappe/erpnext/issues/42310)) ([#42312](https://github.com/frappe/erpnext/issues/42312)) ([cb64c73](cb64c73c9e))
* bin deadlock issue (backport [#42342](https://github.com/frappe/erpnext/issues/42342)) ([#42357](https://github.com/frappe/erpnext/issues/42357)) ([29ee2d4](29ee2d46f0))
* cost center filter by company (backport [#42297](https://github.com/frappe/erpnext/issues/42297)) ([#42299](https://github.com/frappe/erpnext/issues/42299)) ([4c9ce1b](4c9ce1b188))
* extra qty pick in pick list (backport [#42345](https://github.com/frappe/erpnext/issues/42345)) ([#42349](https://github.com/frappe/erpnext/issues/42349)) ([1754adf](1754adfcd6))
* **gross profit:** incorrect valuation rate on different warehouses ([f161e59](f161e59cd7))
* incoming rate zero for supplied items in returned SCR (backport [#42314](https://github.com/frappe/erpnext/issues/42314)) ([#42315](https://github.com/frappe/erpnext/issues/42315)) ([076bf17](076bf17439))
* items not fetching in End Transit entry (backport [#42358](https://github.com/frappe/erpnext/issues/42358)) ([#42361](https://github.com/frappe/erpnext/issues/42361)) ([b5a2e5a](b5a2e5a375))
* keep status as In Progress for RIV for Timeout Error (backport [#42274](https://github.com/frappe/erpnext/issues/42274)) ([#42296](https://github.com/frappe/erpnext/issues/42296)) ([1de66e5](1de66e56ee))
* missing discount on POS Credit Notes ([4055ef9](4055ef92b5))
* not able to cancel the inter transfer DN (backport [#42333](https://github.com/frappe/erpnext/issues/42333)) ([#42340](https://github.com/frappe/erpnext/issues/42340)) ([cf2651d](cf2651dd85))
* not able to submit LCV entry (backport [#42303](https://github.com/frappe/erpnext/issues/42303)) ([#42304](https://github.com/frappe/erpnext/issues/42304)) ([6d098b7](6d098b7302))
* remove doctype link from serial no ledger report (backport [#42327](https://github.com/frappe/erpnext/issues/42327)) ([#42348](https://github.com/frappe/erpnext/issues/42348)) ([b741b2a](b741b2a285))
* removed patch from patches.txt ([c45d11c](c45d11cd60))
* same posting date and time causing incorrect valuation rate (backport [#42351](https://github.com/frappe/erpnext/issues/42351)) ([#42356](https://github.com/frappe/erpnext/issues/42356)) ([62fc428](62fc42803f))
* service item capitalization ([#42188](https://github.com/frappe/erpnext/issues/42188)) ([2ffe7d5](2ffe7d5838))
* show total rows credit row in balance sheet ([0d2ef0d](0d2ef0df7d))
* slowness in reposting dependent vouchers. (backport [#42282](https://github.com/frappe/erpnext/issues/42282)) ([#42292](https://github.com/frappe/erpnext/issues/42292)) ([ef16313](ef16313e0a))
* **Warehouse:** add buttons only if the user can use them ([a2b21c7](a2b21c7570))
* While submitting PCV ensure previous FY is closed (backport [#42284](https://github.com/frappe/erpnext/issues/42284)) ([#42300](https://github.com/frappe/erpnext/issues/42300)) ([e250dcc](e250dcc7c8))

### Features

* configurable depreciation calculation via accounts settings ([#42276](https://github.com/frappe/erpnext/issues/42276)) ([ddd1ca7](ddd1ca7f7c))
* create variant with/without image (backport [#41317](https://github.com/frappe/erpnext/issues/41317)) ([#42343](https://github.com/frappe/erpnext/issues/42343)) ([5f1d6ed](5f1d6ede31))
2024-07-17 05:19:44 +00:00
ruthra kumar
85aca0ef55 Merge pull request #42354 from frappe/version-15-hotfix
chore: release v15
2024-07-17 10:48:30 +05:30
Khushi Rawat
55dc157694 Merge pull request #42360 from frappe/mergify/bp/version-15-hotfix/pr-42188
fix: service item capitalization (backport #42188)
2024-07-17 01:51:54 +05:30
Khushi Rawat
c45d11cd60 fix: removed patch from patches.txt 2024-07-17 01:35:03 +05:30
Khushi Rawat
cfab956811 chore: resolved conflicts 2024-07-16 22:46:54 +05:30
Smit Vora
d1ba12f581 Merge pull request #42362 from frappe/mergify/bp/version-15-hotfix/pr-42352
fix: show total rows credit row in balance sheet (backport #42352)
2024-07-16 19:52:39 +05:30
ljain112
0d2ef0df7d fix: show total rows credit row in balance sheet
(cherry picked from commit 327b19cba6)
2024-07-16 13:58:37 +00:00
mergify[bot]
b5a2e5a375 fix: items not fetching in End Transit entry (backport #42358) (#42361)
fix: items not fetching in End Transit entry (#42358)

(cherry picked from commit 001e5b612b)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-16 18:45:18 +05:30
Khushi Rawat
2ffe7d5838 fix: service item capitalization (#42188)
* feat: capitalize with service expenses only

* chore: added test

* refactor: removed Capitalized In field from asset doc

(cherry picked from commit 81e0b96c30)

# Conflicts:
#	erpnext/assets/doctype/asset/asset.py
2024-07-16 12:16:47 +00:00
mergify[bot]
62fc42803f fix: same posting date and time causing incorrect valuation rate (backport #42351) (#42356)
fix: same posting date and time causing incorrect valuation rate (#42351)

(cherry picked from commit 85d2d34116)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-16 16:30:24 +05:30
mergify[bot]
29ee2d46f0 fix: bin deadlock issue (backport #42342) (#42357)
fix: bin deadlock issue (#42342)

(cherry picked from commit 21df38bf18)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-16 16:30:11 +05:30
mergify[bot]
b741b2a285 fix: remove doctype link from serial no ledger report (backport #42327) (#42348)
fix: remove doctype link from serial no ledger report (#42327)

* fix: remove doctype link from serial no ledger report

* fix: remove doctype link from serial no ledger report

* fix: remove doctype link from serial no ledger report --update

(cherry picked from commit 17b437709c)

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-07-16 16:08:11 +05:30
mergify[bot]
1754adfcd6 fix: extra qty pick in pick list (backport #42345) (#42349)
fix: extra qty pick in pick list (#42345)

(cherry picked from commit 6a50b40976)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-16 15:46:44 +05:30
mergify[bot]
5f1d6ede31 feat: create variant with/without image (backport #41317) (#42343)
feat: create variant with/without image (#41317)

* feat: create variant with/without image

* feat: create variant with/without image

* feat: create variant with/without image

* feat: create variant with/without image

* feat: create variant with/without image

* feat: create variant with/without image

* fix: change the variable name use_same_image to use_template_image

(cherry picked from commit 66b35ec9fb)

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-07-15 22:58:30 +05:30
mergify[bot]
cf2651dd85 fix: not able to cancel the inter transfer DN (backport #42333) (#42340)
fix: not able to cancel the inter transfer DN (#42333)

(cherry picked from commit 6d42cd0f4c)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-15 16:52:28 +05:30
ruthra kumar
29158652db Merge pull request #42338 from frappe/mergify/bp/version-15-hotfix/pr-42294
refactor: make reposting implicit (backport #42294)
2024-07-15 15:37:00 +05:30
ruthra kumar
4968395372 chore: contextual comments
(cherry picked from commit 794a62aecb)
2024-07-15 09:41:57 +00:00
ruthra kumar
8e70aeae4a refactor(test): reposting happens implicitly
(cherry picked from commit c283cda169)
2024-07-15 09:41:57 +00:00
ruthra kumar
4ac174703c refactor(test): no need to assert repost_required flag
Reposting happens implicitly upon 'Update After Submit'

(cherry picked from commit 8f135e9859)
2024-07-15 09:41:57 +00:00
ruthra kumar
804f1d4772 refactor: make reposting implicit
(cherry picked from commit 722ef92324)
2024-07-15 09:41:56 +00:00
ruthra kumar
609a0b81ae Merge pull request #42336 from frappe/mergify/bp/version-15-hotfix/pr-42330
fix: incorrect valuation rate for items from different warehouses in Gross Profit (backport #42330)
2024-07-15 15:11:47 +05:30
ruthra kumar
cc09d0d218 chore: resolve conflict 2024-07-15 14:34:30 +05:30
ruthra kumar
43eec001ee test(gross profit): valuation rate from different warehouse
(cherry picked from commit 577ce5ccd4)

# Conflicts:
#	erpnext/accounts/report/gross_profit/test_gross_profit.py
2024-07-15 08:50:15 +00:00
ruthra kumar
f161e59cd7 fix(gross profit): incorrect valuation rate on different warehouses
(cherry picked from commit f9d2dd0a62)
2024-07-15 08:50:15 +00:00
mergify[bot]
076bf17439 fix: incoming rate zero for supplied items in returned SCR (backport #42314) (#42315)
fix: incoming rate zero for supplied items in returned SCR (#42314)

(cherry picked from commit 61daa318fe)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-15 13:28:06 +05:30
mergify[bot]
cb64c73c9e fix: address and contact filters for SCO and SCR (backport #42310) (#42312)
fix: address and contact filters for SCO and SCR (#42310)

(cherry picked from commit 7656220075)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-13 15:59:46 +05:30
Nabin Hait
ce8b423ad6 chore: release v15 (#42308)
* fix(Warehouse): add buttons only if the user can use them

(cherry picked from commit 10ae5aaf52)

* fix: missing discount on POS Credit Notes

(cherry picked from commit 1049550951)

* chore: rename test suite for payable report

(cherry picked from commit 9474f72776)

* refactor: test suite for item-wise sales register

(cherry picked from commit 3aaa22e672)

* refactor(test): use each instance UOM for assertion

(cherry picked from commit cf4fbfb601)

* fix: slowness in reposting dependent vouchers. (backport #42282) (#42292)

fix: slowness in reposting dependent vouchers. (#42282)

(cherry picked from commit b17696a8ae)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>

* refactor(test): clear old records

* fix: keep status as In Progress for RIV for Timeout Error (backport #42274) (#42296)

fix: keep status as In Progress for RIV for Timeout Error (#42274)

(cherry picked from commit 10280d6140)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>

* fix: cost center filter by company (backport #42297) (#42299)

fix: cost center filter by company (#42297)

(cherry picked from commit 9838f7e6ba)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>

* feat: configurable depreciation calculation via accounts settings (#42276)

* feat: configurable depreciation calculation via accounts settings

* refactor: code optimization

* style: changes in description and label

(cherry picked from commit b04da63aad)

* fix: not able to submit LCV entry (backport #42303) (#42304)

fix: not able to submit LCV entry (#42303)

(cherry picked from commit 9cf92eaeab)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>

* fix: While submitting PCV ensure previous FY is closed (backport #42284) (#42300)

fix: While submitting PCV ensure previous FY is closed (#42284)

(cherry picked from commit d0bbc8ca70)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>

---------

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-07-12 20:47:27 +05:30
mergify[bot]
e250dcc7c8 fix: While submitting PCV ensure previous FY is closed (backport #42284) (#42300)
fix: While submitting PCV ensure previous FY is closed (#42284)

(cherry picked from commit d0bbc8ca70)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-07-12 20:05:03 +05:30
Khushi Rawat
ad228d80d5 Merge pull request #42301 from frappe/mergify/bp/version-15-hotfix/pr-42276
feat: configurable depreciation calculation via accounts settings (backport #42276)
2024-07-12 18:32:37 +05:30
mergify[bot]
6d098b7302 fix: not able to submit LCV entry (backport #42303) (#42304)
fix: not able to submit LCV entry (#42303)

(cherry picked from commit 9cf92eaeab)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-12 17:27:18 +05:30
Khushi Rawat
ddd1ca7f7c feat: configurable depreciation calculation via accounts settings (#42276)
* feat: configurable depreciation calculation via accounts settings

* refactor: code optimization

* style: changes in description and label

(cherry picked from commit b04da63aad)
2024-07-12 08:57:11 +00:00
mergify[bot]
4c9ce1b188 fix: cost center filter by company (backport #42297) (#42299)
fix: cost center filter by company (#42297)

(cherry picked from commit 9838f7e6ba)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-12 13:40:16 +05:30
mergify[bot]
1de66e56ee fix: keep status as In Progress for RIV for Timeout Error (backport #42274) (#42296)
fix: keep status as In Progress for RIV for Timeout Error (#42274)

(cherry picked from commit 10280d6140)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-12 12:14:55 +05:30
ruthra kumar
5e3810b12a Merge pull request #42291 from frappe/mergify/bp/version-15-hotfix/pr-42192
refactor: tests for item wise sales register report (backport #42192)
2024-07-12 09:28:08 +05:30
ruthra kumar
84a8bb3ce5 refactor(test): clear old records 2024-07-11 21:04:34 +05:30
mergify[bot]
ef16313e0a fix: slowness in reposting dependent vouchers. (backport #42282) (#42292)
fix: slowness in reposting dependent vouchers. (#42282)

(cherry picked from commit b17696a8ae)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-11 19:24:09 +05:30
ruthra kumar
61852bd3f6 refactor(test): use each instance UOM for assertion
(cherry picked from commit cf4fbfb601)
2024-07-11 12:34:12 +00:00
ruthra kumar
cd79d33db2 refactor: test suite for item-wise sales register
(cherry picked from commit 3aaa22e672)
2024-07-11 12:34:12 +00:00
ruthra kumar
4f7e0d2955 chore: rename test suite for payable report
(cherry picked from commit 9474f72776)
2024-07-11 12:34:12 +00:00
ruthra kumar
e07bdcee79 Merge pull request #42289 from frappe/mergify/bp/version-15-hotfix/pr-42287
fix: missing discount on POS Credit Notes (backport #42287)
2024-07-11 17:36:02 +05:30
ruthra kumar
4055ef92b5 fix: missing discount on POS Credit Notes
(cherry picked from commit 1049550951)
2024-07-11 11:59:38 +00:00
Raffael Meyer
2751a9a38c Merge pull request #42273 from frappe/mergify/bp/version-15-hotfix/pr-42232
fix(Warehouse): add buttons only if the user can use them (backport #42232)
2024-07-10 15:26:25 +02:00
barredterra
a2b21c7570 fix(Warehouse): add buttons only if the user can use them
(cherry picked from commit 10ae5aaf52)
2024-07-10 11:15:49 +00:00
Frappe PR Bot
f975333970 chore(release): Bumped to Version 15.29.3
## [15.29.3](https://github.com/frappe/erpnext/compare/v15.29.2...v15.29.3) (2024-07-10)

### Bug Fixes

* actual qty in sales order (backport [#42248](https://github.com/frappe/erpnext/issues/42248)) ([#42256](https://github.com/frappe/erpnext/issues/42256)) ([4866958](4866958a96))
* add missing german translations ([2f89461](2f89461ace))
* added filter to show only submitted assets ([19ed6d1](19ed6d1081))
* auto serial and batch bundle not creating for Asset Capitalization (backport [#42231](https://github.com/frappe/erpnext/issues/42231)) ([#42242](https://github.com/frappe/erpnext/issues/42242)) ([7916d64](7916d6436f))
* Billed Qty and Qty to Bill Calculation in Purchase Order Analysis (backport [#42100](https://github.com/frappe/erpnext/issues/42100)) ([#42249](https://github.com/frappe/erpnext/issues/42249)) ([43c7513](43c7513cfe))
* BOM Creator Recursion Error on duplicate save (backport [#41622](https://github.com/frappe/erpnext/issues/41622)) ([#42179](https://github.com/frappe/erpnext/issues/42179)) ([68a39df](68a39dfa33))
* changes as per review ([57896a8](57896a8f99))
* completed DC will not appear in a delivery trip ([0bab609](0bab609a6f))
* completed DC will not appear in a delivery trip ([#41655](https://github.com/frappe/erpnext/issues/41655)) ([a3444a0](a3444a07b7))
* corrected mismatch in the Purchase Receipt Status [#15620](https://github.com/frappe/erpnext/issues/15620) (backport [#42138](https://github.com/frappe/erpnext/issues/42138)) ([#42252](https://github.com/frappe/erpnext/issues/42252)) ([e1b50ef](e1b50efeea))
* correcting balance sheet calculation for zero liabilities and equity ([d48a2c9](d48a2c9f8e))
* correcting balance sheet calculation for zero liabilities and equity ([#41497](https://github.com/frappe/erpnext/issues/41497)) ([2104d90](2104d903aa))
* custom delimiters ([43ad2fe](43ad2fed63))
* download_import_log if rows are greater than 5000 ([4eb251b](4eb251b59a))
* empty item-wise sales/purchase register reports on initial load ([5ac3b34](5ac3b34a6f))
* field position ([a04938d](a04938d5ae))
* group by in item-wise purchase register ([a967d59](a967d59844))
* **Holiday List:** sort holidays on save to avoid disorienting the user (backport [#42236](https://github.com/frappe/erpnext/issues/42236)) ([#42250](https://github.com/frappe/erpnext/issues/42250)) ([b555615](b5556156c1))
* import log preview ([62aac8b](62aac8bb85))
* import status ([71311ff](71311ffd62))
* **Inventory Dimension:** reduce perms for Stock User (backport [#42226](https://github.com/frappe/erpnext/issues/42226)) ([#42243](https://github.com/frappe/erpnext/issues/42243)) ([3cc59e4](3cc59e4a7a))
* manual pick allow to pick more than available stock (backport [#42155](https://github.com/frappe/erpnext/issues/42155)) ([#42159](https://github.com/frappe/erpnext/issues/42159)) ([a7b6530](a7b6530fde))
* Multiple fixes for General Ledger Report ([ca57fd4](ca57fd4255))
* multiple free items on same Item Group ([9352863](93528631c3))
* path of automatically updates the status of asset maintenance log ([5317418](5317418a53))
* Project Status should be Open again if `percent_complete` is not 100 ([90f5c78](90f5c78607))
* provision to enable do not use batch-wise valuation (backport [#42186](https://github.com/frappe/erpnext/issues/42186)) ([#42198](https://github.com/frappe/erpnext/issues/42198)) ([ec881ac](ec881ace76))
* **Putaway Rule:** reduce perms for Stock User (backport [#42227](https://github.com/frappe/erpnext/issues/42227)) ([#42244](https://github.com/frappe/erpnext/issues/42244)) ([b78a97d](b78a97df85))
* remove deprecated field "statement_import_log" ([2f0b97d](2f0b97d91b))
* removed max discount validation for sales return ([ab987e9](ab987e9a86))
* **Stock Entry Type:** reduce perms for Stock User (backport [#42225](https://github.com/frappe/erpnext/issues/42225)) ([#42245](https://github.com/frappe/erpnext/issues/42245)) ([954d9ab](954d9ab154))
* tax on stock_rbnb on repost of Purchase Receipt ([427439c](427439c3f1))
* **tds:** use doctype reference when mapping keys across multiple doctypes ([#42258](https://github.com/frappe/erpnext/issues/42258)) ([8264e3b](8264e3bc77))
* updated logic for calculating tax_withholding_net_total in payment entry ([3fb5c7a](3fb5c7a3a6))
* use standard method to get `_doc_before_save` ([cfda5f6](cfda5f6d0b))
2024-07-10 10:44:06 +00:00
ruthra kumar
38e176160c Merge pull request #42253 from frappe/version-15-hotfix
chore: release v15
2024-07-10 16:12:52 +05:30
ruthra kumar
92bc227743 Merge pull request #42272 from frappe/mergify/bp/version-15-hotfix/pr-42247
fix: don't merge tax into stock account on purchase receipt repost (backport #42247)
2024-07-10 15:48:28 +05:30
ruthra kumar
7b4fd89658 test: tax account heads on PR report without LCV
(cherry picked from commit 9562628ed6)
2024-07-10 09:58:30 +00:00
ruthra kumar
427439c3f1 fix: tax on stock_rbnb on repost of Purchase Receipt
(cherry picked from commit 8633080dff)
2024-07-10 09:58:30 +00:00
Smit Vora
e0460f4891 Merge pull request #42266 from frappe/mergify/bp/version-15-hotfix/pr-42204
fix: Reopen Project if Completion Percentage is Below 100% (backport #42204)
2024-07-10 15:23:19 +05:30
Smit Vora
38da8afa06 Merge pull request #42265 from frappe/mergify/bp/version-15-hotfix/pr-42127
fix: removed max discount validation for sales return (backport #42127)
2024-07-10 15:23:01 +05:30
Smit Vora
afc1df00ae Merge pull request #42263 from frappe/mergify/bp/version-15-hotfix/pr-42258
fix(tds): use doctype reference when mapping keys across multiple doctypes (backport #42258)
2024-07-10 15:22:36 +05:30
DaizyModi
90f5c78607 fix: Project Status should be Open again if percent_complete is not 100
(cherry picked from commit c52fdffdaf)
2024-07-10 07:10:05 +00:00
ljain112
ab987e9a86 fix: removed max discount validation for sales return
(cherry picked from commit db807d433b)
2024-07-10 07:02:02 +00:00
Smit Vora
23053f45de chore: resolve conflicts 2024-07-10 12:26:35 +05:30
Smit Vora
8264e3bc77 fix(tds): use doctype reference when mapping keys across multiple doctypes (#42258)
* fix(tds): use doctype reference when mapping keys across multiple doctypes

* fix: changes as per review

---------

Co-authored-by: ljain112 <ljain112@gmail.com>
(cherry picked from commit 5c0d52f783)

# Conflicts:
#	erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
2024-07-10 06:27:29 +00:00
Sagar Vora
b005d7ac52 Merge pull request #42259 from frappe/mergify/bp/version-15-hotfix/pr-42060
fix: updated logic for calculating tax_withholding_net_total in payment entry (backport #42060)
2024-07-10 11:08:53 +05:30
ljain112
3fb5c7a3a6 fix: updated logic for calculating tax_withholding_net_total in payment entry
(cherry picked from commit c8a34cde7f)
2024-07-10 05:11:35 +00:00
ruthra kumar
b9c0f3f402 Merge pull request #41741 from frappe/mergify/bp/version-15-hotfix/pr-41655
fix: completed DC will not appear in a delivery trip (backport #41655)
2024-07-10 10:33:11 +05:30
ruthra kumar
7d0d1cfd18 Merge pull request #41739 from frappe/mergify/bp/version-15-hotfix/pr-41497
fix: correcting balance sheet calculation for zero liabilities and equity (backport #41497)
2024-07-10 10:30:12 +05:30
ruthra kumar
970bb2bca1 Merge pull request #42164 from frappe/mergify/bp/version-15-hotfix/pr-42162
refactor: remove obsolete function call (backport #42162)
2024-07-10 10:26:15 +05:30
ruthra kumar
759781eefe Merge pull request #42240 from frappe/mergify/bp/version-15-hotfix/pr-42026
fix:  Multiple fixes in Bank Statement Import (backport #42026)
2024-07-10 10:24:57 +05:30
mergify[bot]
b5556156c1 fix(Holiday List): sort holidays on save to avoid disorienting the user (backport #42236) (#42250)
fix(Holiday List): sort holidays on save to avoid disorienting the user (#42236)

fix: sort holidays on save to avoid disorienting the user
(cherry picked from commit ad137250fc)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2024-07-09 17:53:38 +05:30
Khushi Rawat
2deea33a85 Merge pull request #42257 from frappe/mergify/bp/version-15-hotfix/pr-42233
fix: added filter to show only submitted assets (backport #42233)
2024-07-09 16:59:20 +05:30
Khushi Rawat
19ed6d1081 fix: added filter to show only submitted assets
(cherry picked from commit cd3a900495)
2024-07-09 11:12:01 +00:00
mergify[bot]
43c7513cfe fix: Billed Qty and Qty to Bill Calculation in Purchase Order Analysis (backport #42100) (#42249)
fix: Billed Qty and Qty to Bill Calculation in Purchase Order Analysis (#42100)

(cherry picked from commit 0c76a8cc9e)

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-07-09 16:17:57 +05:30
mergify[bot]
e1b50efeea fix: corrected mismatch in the Purchase Receipt Status #15620 (backport #42138) (#42252)
* fix: corrected mismatch in the Purchase Receipt Status #15620 (#42138)

(cherry picked from commit 623b4c21cd)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py

* chore: fix conflicts

---------

Co-authored-by: Poorvi-R-Bhat <poorvi.r.bhat@gmail.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-09 16:17:35 +05:30
mergify[bot]
4866958a96 fix: actual qty in sales order (backport #42248) (#42256)
fix: actual qty in sales order (#42248)

(cherry picked from commit 1186ee128c)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-09 16:17:20 +05:30
mergify[bot]
3cc59e4a7a fix(Inventory Dimension): reduce perms for Stock User (backport #42226) (#42243)
* fix(Inventory Dimension): reduce perms for Stock User (#42226)

(cherry picked from commit edc1f84250)

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

* chore: fix conflicts

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-09 14:52:30 +05:30
mergify[bot]
b78a97df85 fix(Putaway Rule): reduce perms for Stock User (backport #42227) (#42244)
* fix(Putaway Rule): reduce perms for Stock User (#42227)

(cherry picked from commit 96b275c61e)

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

* chore: fix conflicts

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-09 14:52:17 +05:30
mergify[bot]
954d9ab154 fix(Stock Entry Type): reduce perms for Stock User (backport #42225) (#42245)
* fix(Stock Entry Type): reduce perms for Stock User (#42225)

(cherry picked from commit 4936514c8d)

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

* chore: fix conflicts

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-09 14:52:06 +05:30
mergify[bot]
68a39dfa33 fix: BOM Creator Recursion Error on duplicate save (backport #41622) (#42179)
fix: BOM Creator Recursion Error on duplicate save (#41622)

(cherry picked from commit 9cfe09bdf6)

Co-authored-by: Richard Case <110036763+casesolved-co-uk@users.noreply.github.com>
2024-07-09 12:19:10 +05:30
mergify[bot]
7916d6436f fix: auto serial and batch bundle not creating for Asset Capitalization (backport #42231) (#42242)
fix: auto serial and batch bundle not creating for Asset Capitalization (#42231)

(cherry picked from commit 5f53ad3117)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-09 12:13:19 +05:30
ruthra kumar
f281e064f2 chore: resolve conflict 2024-07-09 10:39:02 +05:30
ljain112
57896a8f99 fix: changes as per review
(cherry picked from commit ba08b2e8e8)
2024-07-09 04:40:37 +00:00
ljain112
a04938d5ae fix: field position
(cherry picked from commit 98ad01c736)

# Conflicts:
#	erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json
2024-07-09 04:40:37 +00:00
ljain112
4eb251b59a fix: download_import_log if rows are greater than 5000
(cherry picked from commit 2112d8f772)
2024-07-09 04:40:37 +00:00
ljain112
62aac8bb85 fix: import log preview
(cherry picked from commit 1ad264de48)
2024-07-09 04:40:37 +00:00
ljain112
71311ffd62 fix: import status
(cherry picked from commit 07f68884e0)
2024-07-09 04:40:37 +00:00
ljain112
2f0b97d91b fix: remove deprecated field "statement_import_log"
(cherry picked from commit b202409428)

# Conflicts:
#	erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json
2024-07-09 04:40:37 +00:00
ljain112
43ad2fed63 fix: custom delimiters
(cherry picked from commit 13fb560401)

# Conflicts:
#	erpnext/accounts/doctype/bank_statement_import/bank_statement_import.json
2024-07-09 04:40:37 +00:00
Raffael Meyer
3f82ce2e77 Merge pull request #42235 from barredterra/de_transaltions_08062024 2024-07-08 19:50:51 +02:00
barredterra
2f89461ace fix: add missing german translations 2024-07-08 19:34:39 +02:00
mergify[bot]
ec881ace76 fix: provision to enable do not use batch-wise valuation (backport #42186) (#42198)
* fix: provision to enable do not use batch-wise valuation (#42186)

fix: provision to enable do not use batchwise valuation
(cherry picked from commit f06ba0cc36)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-08 11:02:02 +05:30
Sagar Vora
b6bee319da Merge pull request #42215 from frappe/mergify/bp/version-15-hotfix/pr-42146
fix: Multiple fixes for General Ledger Report (backport #42146)
2024-07-08 10:06:50 +05:30
Sagar Vora
ca57fd4255 fix: Multiple fixes for General Ledger Report
(cherry picked from commit 97f02015c2)
2024-07-08 04:36:11 +00:00
Frappe PR Bot
c9e3dee5b2 chore(release): Bumped to Version 15.29.2
## [15.29.2](https://github.com/frappe/erpnext/compare/v15.29.1...v15.29.2) (2024-07-05)

### Bug Fixes

* blank item-wise sales/purchase register reports on first load ([8baef24](8baef24541))
* group by in item-wise purchase register ([20d481d](20d481de5e))
2024-07-05 02:47:13 +00:00
ruthra kumar
a8cd49112d Merge pull request #42191 from frappe/mergify/bp/version-15/pr-41975
fix: group by in item-wise purchase register (backport #41975)
2024-07-05 08:15:58 +05:30
ruthra kumar
8baef24541 fix: blank item-wise sales/purchase register reports on first load 2024-07-05 07:43:13 +05:30
Nihantra C. Patel
20d481de5e fix: group by in item-wise purchase register
(cherry picked from commit 3fab00135b)
2024-07-05 02:05:29 +00:00
ruthra kumar
276b9bc2b9 Merge pull request #42185 from frappe/mergify/bp/version-15-hotfix/pr-42183
fix: empty item-wise sales/purchase register reports on initial load (backport #42183)
2024-07-04 15:12:15 +05:30
ruthra kumar
5ac3b34a6f fix: empty item-wise sales/purchase register reports on initial load
(cherry picked from commit ee862126e4)
2024-07-04 09:36:58 +00:00
ruthra kumar
8ab49f4d9d Merge pull request #42181 from frappe/mergify/bp/version-15-hotfix/pr-41975
fix: group by in item-wise purchase register (backport #41975)
2024-07-04 14:59:52 +05:30
Nihantra C. Patel
b3715b2b82 Merge branch 'version-15-hotfix' into mergify/bp/version-15-hotfix/pr-41655 2024-07-04 14:43:29 +05:30
Nihantra C. Patel
a967d59844 fix: group by in item-wise purchase register
(cherry picked from commit 3fab00135b)
2024-07-04 09:09:09 +00:00
ruthra kumar
cf937edc4e Merge pull request #42173 from frappe/mergify/bp/version-15-hotfix/pr-42143
refactor: validation to prevent recursion with mixed conditions (backport #42143)
2024-07-04 09:20:23 +05:30
ruthra kumar
12bec3be9d test: validation on mixed condition and recursion on pricing rule
(cherry picked from commit eb4af58bf0)
2024-07-03 15:27:43 +00:00
ruthra kumar
0774607f52 test: validation on mixed condition with recursion
(cherry picked from commit 9bd4e7b709)
2024-07-03 15:27:43 +00:00
ruthra kumar
cfda5f6d0b fix: use standard method to get _doc_before_save
(cherry picked from commit 9d7be293ae)
2024-07-03 15:27:43 +00:00
ruthra kumar
4ecb02cb41 refactor: validation to prevent recursion with mixed conditions
(cherry picked from commit 406dfd528f)
2024-07-03 15:27:43 +00:00
ruthra kumar
fcfe78b3bc Merge pull request #42170 from frappe/mergify/bp/version-15-hotfix/pr-42165
fix: multiple free items on same Item Group (backport #42165)
2024-07-03 20:55:45 +05:30
Frappe PR Bot
7568af67e9 chore(release): Bumped to Version 15.29.1
## [15.29.1](https://github.com/frappe/erpnext/compare/v15.29.0...v15.29.1) (2024-07-03)

### Bug Fixes

* path of automatically updates the status of asset maintenance log ([473aaf4](473aaf4e5b))
2024-07-03 14:56:03 +00:00
Raffael Meyer
5dbdcb1158 Merge pull request #42171 from frappe/mergify/bp/version-15/pr-42168
fix: path of automatically updates the status of asset maintenance log (backport #42157) (backport #42168)
2024-07-03 16:54:44 +02:00
Nihantra C. Patel
473aaf4e5b fix: path of automatically updates the status of asset maintenance log
(cherry picked from commit 909aa8f359)
(cherry picked from commit 5317418a53)
2024-07-03 14:15:05 +00:00
Raffael Meyer
de130cb1ab Merge pull request #42168 from frappe/mergify/bp/version-15-hotfix/pr-42157
fix: path of automatically updates the status of asset maintenance log (backport #42157)
2024-07-03 16:11:11 +02:00
ruthra kumar
93528631c3 fix: multiple free items on same Item Group
(cherry picked from commit c4ae0d283f)
2024-07-03 12:28:38 +00:00
Nihantra C. Patel
5317418a53 fix: path of automatically updates the status of asset maintenance log
(cherry picked from commit 909aa8f359)
2024-07-03 10:55:50 +00:00
Markus Lobedann
4f623c3b66 refactor: remove obsolete function call (#42162)
(cherry picked from commit 4512432816)
2024-07-03 10:22:39 +00:00
mergify[bot]
a7b6530fde fix: manual pick allow to pick more than available stock (backport #42155) (#42159)
fix: manual pick allow to pick more than available stock (#42155)

(cherry picked from commit 938dd4b2aa)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-03 15:42:37 +05:30
Frappe PR Bot
5812577854 chore(release): Bumped to Version 15.29.0
# [15.29.0](https://github.com/frappe/erpnext/compare/v15.28.2...v15.29.0) (2024-07-03)

### Bug Fixes

* add auto-update for overdue status ([#42105](https://github.com/frappe/erpnext/issues/42105)) ([317cc03](317cc0358c))
* add string for translation (backport [#41903](https://github.com/frappe/erpnext/issues/41903)) ([#41963](https://github.com/frappe/erpnext/issues/41963)) ([48dc24b](48dc24b9bf))
* always post to tax account heads if LCV is booked ([706a6c1](706a6c1ad7))
* batch picking in pick list based on Stock Settings (backport [#42021](https://github.com/frappe/erpnext/issues/42021)) ([#42134](https://github.com/frappe/erpnext/issues/42134)) ([a45f8ca](a45f8ca5fd))
* batch reset while making SABB (backport [#42076](https://github.com/frappe/erpnext/issues/42076)) ([#42123](https://github.com/frappe/erpnext/issues/42123)) ([c3f5a49](c3f5a494f3))
* decimal issue in pick list (backport [#41972](https://github.com/frappe/erpnext/issues/41972)) ([#41982](https://github.com/frappe/erpnext/issues/41982)) ([9945a90](9945a90b3f))
* **Delivery Note:** only show permitted actions ([cef6d0d](cef6d0d74d))
* do not show zero balance stock items in stock balance report (backport [#41958](https://github.com/frappe/erpnext/issues/41958)) ([#41961](https://github.com/frappe/erpnext/issues/41961)) ([c10b123](c10b123a81))
* expense account from item group not fetched (backport [#41957](https://github.com/frappe/erpnext/issues/41957)) ([#41962](https://github.com/frappe/erpnext/issues/41962)) ([760b2e2](760b2e24f2))
* fixed asset value in Fixed Asset Register (backport [#41930](https://github.com/frappe/erpnext/issues/41930)) ([#42027](https://github.com/frappe/erpnext/issues/42027)) ([f2feeaf](f2feeaf264))
* handle none type object error ([b0aef9e](b0aef9e42b))
* incorrect against_account upon reposting ([a41577a](a41577a1cd))
* incorrect Difference Amount (backport [#42008](https://github.com/frappe/erpnext/issues/42008)) ([#42013](https://github.com/frappe/erpnext/issues/42013)) ([838cc5b](838cc5b72a))
* incorrect discount on other item ([77f4199](77f4199e2a))
* incorrect dr/cr on Adv Payment against Journals ([4e74257](4e74257ba9))
* incorrect time period in asset depreciation schedule (backport [#41805](https://github.com/frappe/erpnext/issues/41805)) ([#42043](https://github.com/frappe/erpnext/issues/42043)) ([cf4d4ba](cf4d4ba3e9))
* lead status filter (backport [#41816](https://github.com/frappe/erpnext/issues/41816)) ([#42046](https://github.com/frappe/erpnext/issues/42046)) ([3536a75](3536a754ff))
* manufacturing date issue in the batch (backport [#42034](https://github.com/frappe/erpnext/issues/42034)) ([#42037](https://github.com/frappe/erpnext/issues/42037)) ([a981633](a981633d94))
* move condition for shipment ([2180239](21802396ce))
* not able to make purchase return (backport [#42053](https://github.com/frappe/erpnext/issues/42053)) ([#42055](https://github.com/frappe/erpnext/issues/42055)) ([8a91bf3](8a91bf3154))
* pricing rule with and without 'apply multiple' and priority ([f3aa885](f3aa885488))
* provisional entry for non stock items ([d61dab8](d61dab8569))
* Re-open allows SO's to be over credit limit ([7fcb0f5](7fcb0f578a))
* refactor Asset Repair and Stock Entry linkage to resolve amendme… (backport [#41919](https://github.com/frappe/erpnext/issues/41919)) ([#42058](https://github.com/frappe/erpnext/issues/42058)) ([97c49b9](97c49b93b6))
* reload asset when creating asset depreciation ([7b5d504](7b5d5043c5))
* reposting file attachment permission issue (backport [#42068](https://github.com/frappe/erpnext/issues/42068)) ([#42075](https://github.com/frappe/erpnext/issues/42075)) ([1f3374f](1f3374fcdf))
* resolve gl entries duplication in asset purchase workflow (backport [#41845](https://github.com/frappe/erpnext/issues/41845)) ([#42120](https://github.com/frappe/erpnext/issues/42120)) ([58e18e2](58e18e2b1f))
* **Sales Order:** only show permitted actions ([a0011c5](a0011c5b52))
* show zero stock items filter in the stock balance report (backport [#42147](https://github.com/frappe/erpnext/issues/42147)) ([#42152](https://github.com/frappe/erpnext/issues/42152)) ([11ebbf2](11ebbf2a9c))
* stock qty validation in SCR (backport [#42124](https://github.com/frappe/erpnext/issues/42124)) ([#42133](https://github.com/frappe/erpnext/issues/42133)) ([d9e62fe](d9e62fef21))
* Stock Reservation Entry was not getting created (backport [#42033](https://github.com/frappe/erpnext/issues/42033)) ([#42035](https://github.com/frappe/erpnext/issues/42035)) ([e278fc6](e278fc683f))
* **test:** incorrect field for customer default billing currency ([3b15708](3b15708f18))
* this.frm.events.update_cost is not a function (backport [#41960](https://github.com/frappe/erpnext/issues/41960)) ([#41965](https://github.com/frappe/erpnext/issues/41965)) ([3b4d397](3b4d39766f))
* timeout error while submitting JV (backport [#42040](https://github.com/frappe/erpnext/issues/42040)) ([#42099](https://github.com/frappe/erpnext/issues/42099)) ([a0e06a4](a0e06a4ba5))
* timeout while cancelling LCV (backport [#42030](https://github.com/frappe/erpnext/issues/42030)) (backport [#42031](https://github.com/frappe/erpnext/issues/42031)) ([#42032](https://github.com/frappe/erpnext/issues/42032)) ([068de08](068de08bbb))
* unhide serial no field (backport [#42045](https://github.com/frappe/erpnext/issues/42045)) ([#42047](https://github.com/frappe/erpnext/issues/42047)) ([482832f](482832f3c2))
* valuation rate for the legacy batches (backport [#42011](https://github.com/frappe/erpnext/issues/42011)) ([#42020](https://github.com/frappe/erpnext/issues/42020)) ([f6be19c](f6be19cb7c))
* Wrong Delete Batch on Purchase Receipt (backport [#42007](https://github.com/frappe/erpnext/issues/42007)) ([#42012](https://github.com/frappe/erpnext/issues/42012)) ([68b318a](68b318a94b))

### Features

* accounting dimension filters in gp report ([fe9dffb](fe9dffb271))
* default account head for operating cost (backport [#41985](https://github.com/frappe/erpnext/issues/41985)) ([#41987](https://github.com/frappe/erpnext/issues/41987)) ([44c1671](44c16713ba))
* **gp:** group by cost center ([068ae87](068ae87b8d))
* Turkish Chart Of Accounts (backport [#41756](https://github.com/frappe/erpnext/issues/41756)) ([#42028](https://github.com/frappe/erpnext/issues/42028)) ([63b26e6](63b26e679b))

### Performance Improvements

* code optimization to handle large asset creation (backport [#42018](https://github.com/frappe/erpnext/issues/42018)) ([#42025](https://github.com/frappe/erpnext/issues/42025)) ([c27f272](c27f272f06))
* dont run queries unnecessarily, improved filters ([#41993](https://github.com/frappe/erpnext/issues/41993)) ([b59c91a](b59c91a341))
* Performance optmization for Purchase Invoice submission (backport [#40263](https://github.com/frappe/erpnext/issues/40263)) ([#41946](https://github.com/frappe/erpnext/issues/41946)) ([d396c18](d396c18689))
2024-07-03 05:06:09 +00:00
ruthra kumar
ebb8d90b4d Merge pull request #42142 from frappe/version-15-hotfix
chore: release v15
2024-07-03 10:33:25 +05:30
mergify[bot]
11ebbf2a9c fix: show zero stock items filter in the stock balance report (backport #42147) (#42152)
fix: show zero stock items filter in the stock balance report (#42147)

(cherry picked from commit 1dae2156e3)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-03 10:09:09 +05:30
ruthra kumar
c9cde259ec Merge branch 'version-15' into version-15-hotfix 2024-07-03 10:06:49 +05:30
ruthra kumar
b15ec238c9 Merge pull request #42151 from frappe/mergify/bp/version-15-hotfix/pr-42073
fix: always post to tax account heads if LCV is booked (backport #42073)
2024-07-03 07:23:08 +05:30
ruthra kumar
09c39face8 Merge pull request #42140 from nabinhait/provisional_entry_fix3
fix: provisional entry for non stock items
2024-07-03 07:20:56 +05:30
ruthra kumar
c3cc363648 refactor(test): fix flaky test
(cherry picked from commit 0e256b8b29)
2024-07-03 01:35:15 +00:00
ruthra kumar
e55fd7204a refactor(test): cleanup test data
(cherry picked from commit 6ba6b5aa33)
2024-07-03 01:35:15 +00:00
ruthra kumar
b7cbafae14 test: Repost should not merge expense accounts from LCV
(cherry picked from commit fa56555150)
2024-07-03 01:35:15 +00:00
ruthra kumar
706a6c1ad7 fix: always post to tax account heads if LCV is booked
(cherry picked from commit 0fcd5d5130)
2024-07-03 01:35:15 +00:00
Khushi Rawat
28e8bb7085 Merge pull request #42130 from frappe/mergify/bp/version-15-hotfix/pr-42105
fix: add auto-update for overdue status (backport #42105)
2024-07-02 23:17:04 +05:30
Khushi Rawat
8b3ffc9949 Merge pull request #42145 from frappe/mergify/bp/version-15-hotfix/pr-42144
fix: handle none type object error (backport #42144)
2024-07-02 16:54:36 +05:30
Khushi Rawat
b0aef9e42b fix: handle none type object error
(cherry picked from commit 6760c9c4e2)
2024-07-02 11:08:42 +00:00
Nabin Hait
d61dab8569 fix: provisional entry for non stock items 2024-07-02 16:10:03 +05:30
mergify[bot]
a45f8ca5fd fix: batch picking in pick list based on Stock Settings (backport #42021) (#42134)
fix: batch picking in pick list based on Stock Settings (#42021)

(cherry picked from commit 97c9941143)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-02 10:51:06 +05:30
mergify[bot]
d9e62fef21 fix: stock qty validation in SCR (backport #42124) (#42133)
fix: stock qty validation in SCR (#42124)

(cherry picked from commit 99f2735ad3)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-01 19:12:58 +05:30
mergify[bot]
3b4d39766f fix: this.frm.events.update_cost is not a function (backport #41960) (#41965)
* fix: this.frm.events.update_cost is not a function (#41960)

(cherry picked from commit d5ed4582c3)

# Conflicts:
#	erpnext/manufacturing/doctype/workstation/workstation.json
#	erpnext/manufacturing/doctype/workstation/workstation.py

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-01 18:18:58 +05:30
ruthra kumar
61c0ce6ca8 Merge pull request #42132 from frappe/mergify/bp/version-15-hotfix/pr-42126
fix: Re-open allows SO's to be over credit limit (backport #42126)
2024-07-01 17:21:19 +05:30
ruthra kumar
bff99d89b9 chore: resolve conflict 2024-07-01 17:05:22 +05:30
ruthra kumar
b63eab8cbb test: credit check on Sales Order re-open
(cherry picked from commit 60694e09c4)

# Conflicts:
#	erpnext/selling/doctype/sales_order/test_sales_order.py
2024-07-01 11:33:36 +00:00
ruthra kumar
7fcb0f578a fix: Re-open allows SO's to be over credit limit
(cherry picked from commit 5eed781263)
2024-07-01 11:33:36 +00:00
Khushi Rawat
317cc0358c fix: add auto-update for overdue status (#42105)
* fix: auto-update for overdue status

* chore: use qb.update

(cherry picked from commit c5e474f4f5)
2024-07-01 11:27:02 +00:00
mergify[bot]
58e18e2b1f fix: resolve gl entries duplication in asset purchase workflow (backport #41845) (#42120)
* fix: resolve gl entries duplication in asset purchase workflow (#41845)

* fix: resolve gl entries duplication in asset purchase workflow

* fix: prevent duplicate entry when creating purchase receipt from purchase invoice

* chore: test case added

* fix: fixed missing asset category issue

(cherry picked from commit 55a4bd469b)

* fix: use f-string instead of format call

---------

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-07-01 16:54:14 +05:30
mergify[bot]
97c49b93b6 fix: refactor Asset Repair and Stock Entry linkage to resolve amendme… (backport #41919) (#42058)
* fix: refactor Asset Repair and Stock Entry linkage to resolve amendme… (#41919)

* fix: refactor Asset Repair and Stock Entry linkage to resolve amendment issues

* chore: added missing patch to patches.txt

* chore: fixing previous changes

* chore: fixing minor issues

* fix: code changes to enhance efficiency

* chore: replaced frappe.qb with db.sql because of conflict

* fix: minor changes

(cherry picked from commit ba79e68190)

# Conflicts:
#	erpnext/assets/doctype/asset_repair/asset_repair.json
#	erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json
#	erpnext/patches.txt
#	erpnext/stock/doctype/stock_entry/stock_entry.json

* chore: fixed conflicts

* fix: removed unmerged patches

* fix: use f-string instead of format call

---------

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-07-01 16:53:29 +05:30
mergify[bot]
c3f5a494f3 fix: batch reset while making SABB (backport #42076) (#42123)
fix: batch reset while making SABB (#42076)

(cherry picked from commit 8f424528dd)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-01 15:12:36 +05:30
mergify[bot]
a0e06a4ba5 fix: timeout error while submitting JV (backport #42040) (#42099)
* fix: timeout error while submitting JV (#42040)

(cherry picked from commit 32bdcdb08f)

# Conflicts:
#	erpnext/accounts/doctype/account/account.json
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
#	erpnext/accounts/utils.py
#	erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-07-01 14:15:13 +05:30
ruthra kumar
deaeb103d5 Merge pull request #42106 from frappe/mergify/bp/version-15-hotfix/pr-42029
fix: incorrect ledger entries on Advance payment against Journals for customer (backport #42029)
2024-06-30 05:55:54 +05:30
ruthra kumar
3c58e0af50 refactor: handle purchase invoice as reference
(cherry picked from commit 9ec6aef95d)
2024-06-30 00:08:42 +00:00
ruthra kumar
cb703ff17c chore: better test name
(cherry picked from commit ad7efd5939)
2024-06-30 00:08:41 +00:00
ruthra kumar
6a0111c7db test: advance payment entry against journal - supplier type
(cherry picked from commit 1b384b9942)
2024-06-30 00:08:41 +00:00
ruthra kumar
e00348fd52 test: advance payment against journal entry - customer type
(cherry picked from commit 5e84272cf9)
2024-06-30 00:08:41 +00:00
ruthra kumar
4e74257ba9 fix: incorrect dr/cr on Adv Payment against Journals
(cherry picked from commit f6c1dffb35)
2024-06-30 00:08:41 +00:00
Frappe PR Bot
fb76daaf9e chore(release): Bumped to Version 15.28.2
## [15.28.2](https://github.com/frappe/erpnext/compare/v15.28.1...v15.28.2) (2024-06-29)

### Bug Fixes

* expense account from item group not fetched (backport [#41957](https://github.com/frappe/erpnext/issues/41957)) ([#41962](https://github.com/frappe/erpnext/issues/41962)) ([d310074](d310074222))
2024-06-29 16:18:59 +00:00
rohitwaghchaure
0df706f14d Merge pull request #42101 from frappe/mergify/bp/version-15/pr-41962
fix: expense account from item group not fetched (backport #41957) (backport #41962)
2024-06-29 21:47:42 +05:30
mergify[bot]
d310074222 fix: expense account from item group not fetched (backport #41957) (#41962)
fix: expense account from item group not fetched

(cherry picked from commit 86ebe58231)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
(cherry picked from commit 760b2e24f2)
2024-06-29 05:31:05 +00:00
mergify[bot]
760b2e24f2 fix: expense account from item group not fetched (backport #41957) (#41962)
fix: expense account from item group not fetched

(cherry picked from commit 86ebe58231)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2024-06-29 10:59:57 +05:30
mergify[bot]
1f3374fcdf fix: reposting file attachment permission issue (backport #42068) (#42075)
* fix: reposting file attachment permission issue (#42068)

(cherry picked from commit 03e674e21d)

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

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-29 07:32:45 +05:30
mergify[bot]
d396c18689 perf: Performance optmization for Purchase Invoice submission (backport #40263) (#41946)
* perf: Optimization for providional gl entries

(cherry picked from commit d7b738ff61)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py

* perf: Performance optimization for validating budget

(cherry picked from commit f204d810bb)

# Conflicts:
#	erpnext/accounts/doctype/budget/budget.py

* perf: Cached accounting dimensions details

(cherry picked from commit 8cd8b8f885)

* perf: Optimzed code for merging similar gl entries

(cherry picked from commit aa75a60142)

* fix: linter issues

(cherry picked from commit acc0b2faf8)

* perf: Cache accounting dimension filter map

(cherry picked from commit e4bd173875)

# Conflicts:
#	erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py

* fix: minor fixes

(cherry picked from commit 5cd9bf3bda)

* perf: skip unnecessary validation while transaction  cancellation

(cherry picked from commit 05385e4acb)

* perf: refactored handling provisional gl entries for non-stock items

(cherry picked from commit 49c74369a5)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py

* perf: validate expense against budget only if budget exists

(cherry picked from commit c15b2d5490)

* perf: Get bin details only for stock items

(cherry picked from commit 6ff9e6ee84)

# Conflicts:
#	erpnext/stock/get_item_details.py

* fix: added index for price_list column in Item Price

(cherry picked from commit d279e23623)

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

* perf: Caching in checking allowance for qty and amount

(cherry picked from commit 8d682fa884)

* perf: Caching in gl entry

(cherry picked from commit b07769d8d7)

# Conflicts:
#	erpnext/accounts/doctype/gl_entry/gl_entry.py

* chore: resolve conflicts

* chore: resolve conflict in purchase_invoice.py

---------

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2024-06-27 17:32:43 +05:30
mergify[bot]
8a91bf3154 fix: not able to make purchase return (backport #42053) (#42055)
fix: not able to make purchase return (#42053)

(cherry picked from commit 9738c04ef0)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-27 16:31:06 +05:30
mergify[bot]
482832f3c2 fix: unhide serial no field (backport #42045) (#42047)
* fix: unhide serial no field (#42045)

(cherry picked from commit 80c6981cfa)

# Conflicts:
#	erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json

* fix: resolved conflicts

---------

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-06-26 18:01:33 +05:30
mergify[bot]
3536a754ff fix: lead status filter (backport #41816) (#42046)
fix: lead status filter (#41816)

(cherry picked from commit 8ae2b8ff8c)

Co-authored-by: Nihantra C. Patel <141945075+Nihantra-Patel@users.noreply.github.com>
2024-06-26 17:47:30 +05:30
mergify[bot]
b29435744f chore: patch to enable total number of booked depreciations field (backport #41940) (#42042)
* chore: patch to enable total number of booked depreciations field (#41940)

* chore: patch to enable total number of booked depreciations field

* fix: conflict resolved

* refactor: replaced fb_row.db_set with set_value

(cherry picked from commit 5fdd1d3278)

# Conflicts:
#	erpnext/patches.txt

* fix: resolved conflicts

* fix: removed unmerged patches

---------

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-06-26 17:43:57 +05:30
mergify[bot]
cf4d4ba3e9 fix: incorrect time period in asset depreciation schedule (backport #41805) (#42043)
fix: incorrect time period in asset depreciation schedule (#41805)

* fix(wip): depreciation calculation for existing asset

* fix(wip): added validation for incorrect depreciation period

* fix: depreciation schedule time period issue for existing asset

* chore: run pre-commit checks and apply fixes

* style: apply formatting changes

* style: made some necessary changes

* chore: modified test

(cherry picked from commit 625f16dee0)

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-06-26 16:51:16 +05:30
mergify[bot]
c27f272f06 perf: code optimization to handle large asset creation (backport #42018) (#42025)
perf: code optimization to handle large asset creation (#42018)

(cherry picked from commit 5738d93f95)

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-06-26 16:51:07 +05:30
mergify[bot]
63b26e679b feat: Turkish Chart Of Accounts (backport #41756) (#42028)
* feat: Create Turkish Chart Of Accounts

(cherry picked from commit 5c8ea86a3f)

* feat: Create Turkish Chart Of Accounts

(cherry picked from commit b401ba2c26)

---------

Co-authored-by: fzozyurt <fzozyurt@outlook.com>
2024-06-26 16:50:41 +05:30
mergify[bot]
f2feeaf264 fix: fixed asset value in Fixed Asset Register (backport #41930) (#42027)
fix: fixed asset value in Fixed Asset Register (#41930)

(cherry picked from commit 1c643a0ead)

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-06-26 16:49:55 +05:30
mergify[bot]
a981633d94 fix: manufacturing date issue in the batch (backport #42034) (#42037)
* fix: manufacturing date issue in the batch (#42034)

(cherry picked from commit eca3e02f8d)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-26 13:00:36 +05:30
mergify[bot]
e278fc683f fix: Stock Reservation Entry was not getting created (backport #42033) (#42035)
fix: Stock Reservation Entry was not getting created (#42033)

(cherry picked from commit 1a9899b32b)

Co-authored-by: Poorvi-R-Bhat <poorvi.r.bhat@gmail.com>
2024-06-26 09:11:01 +05:30
mergify[bot]
068de08bbb fix: timeout while cancelling LCV (backport #42030) (backport #42031) (#42032)
fix: timeout while cancelling LCV (backport #42030) (#42031)

fix: timeout while cancelling LCV (#42030)

fix: timeout while canelling LCV
(cherry picked from commit 21bf7fd1f8)

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-06-25 18:42:43 +05:30
mergify[bot]
f6be19cb7c fix: valuation rate for the legacy batches (backport #42011) (#42020)
fix: valuation rate for the legacy batches (#42011)

(cherry picked from commit 9ab333d105)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-25 13:07:03 +05:30
mergify[bot]
838cc5b72a fix: incorrect Difference Amount (backport #42008) (#42013)
fix: incorrect Difference Amount (#42008)

(cherry picked from commit 7d91c6cbd5)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-24 17:04:24 +05:30
mergify[bot]
68b318a94b fix: Wrong Delete Batch on Purchase Receipt (backport #42007) (#42012)
fix: Wrong Delete Batch on Purchase Receipt (#42007)

(cherry picked from commit d50487ce53)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-24 17:04:13 +05:30
Deepesh Garg
6a0ab23c87 Merge pull request #42006 from frappe/mergify/bp/version-15-hotfix/pr-41751
feat: accounting dimension filters in gp report (#41751)
2024-06-24 11:24:24 +05:30
Dany Robert
068ae87b8d feat(gp): group by cost center
(cherry picked from commit e26bc17c75)
2024-06-24 05:22:39 +00:00
Dany Robert
fe9dffb271 feat: accounting dimension filters in gp report
(cherry picked from commit d165638bbb)
2024-06-24 05:22:39 +00:00
ruthra kumar
521cfb3d4e Merge pull request #41998 from frappe/mergify/bp/version-15-hotfix/pr-41976
fix: pricing rule with and without 'apply multiple' and priority (backport #41976)
2024-06-23 05:49:07 +05:30
ruthra kumar
bc6cbb2656 Merge pull request #41997 from frappe/mergify/bp/version-15-hotfix/pr-41844
refactor: allow foreign currency advance accounts (backport #41844)
2024-06-23 05:43:10 +05:30
ruthra kumar
f52f726e06 test: priority takes effect on with and without apply multiple
(cherry picked from commit efebc3662e)
2024-06-23 00:02:10 +00:00
ruthra kumar
f3aa885488 fix: pricing rule with and without 'apply multiple' and priority
Either all of the pricing rules identified for an item should have
'apply multiple' enabled. If not, Priority is applied and only the
highest priority is applied

(cherry picked from commit 5e875b238c)
2024-06-23 00:02:10 +00:00
ruthra kumar
c45ce75f57 refactor(test): make and use a different party for subscription
(cherry picked from commit 3fabf4aaa4)
2024-06-22 23:58:11 +00:00
ruthra kumar
6dbe820416 refactor(test): enfore use of customer/supplier master
While using advance accounts in foreign currency, always use
Customer/Supplier master to maintain them

(cherry picked from commit 64e63887be)
2024-06-22 23:58:11 +00:00
ruthra kumar
3b15708f18 fix(test): incorrect field for customer default billing currency
(cherry picked from commit c696d13a5e)
2024-06-22 23:58:10 +00:00
ruthra kumar
a1ebd16284 chore: remove dead code
(cherry picked from commit 7e318c0132)
2024-06-22 23:58:10 +00:00
ruthra kumar
d1679d4663 chore: fix test data
(cherry picked from commit 07d59443b7)
2024-06-22 23:58:10 +00:00
ruthra kumar
2bd10d388f refactor: better error messages
(cherry picked from commit 83ff94b9b8)
2024-06-22 23:58:10 +00:00
ruthra kumar
545d0b9730 refactor: validation in Supplier Group
(cherry picked from commit 107b614518)
2024-06-22 23:58:10 +00:00
ruthra kumar
4bde345399 refactor: validation in customer group
(cherry picked from commit 4f9a228175)
2024-06-22 23:58:10 +00:00
ruthra kumar
78ad3f6cdc refactor: validation to force accounts to be on same currency
(cherry picked from commit 0f0b4d88bc)
2024-06-22 23:58:10 +00:00
ruthra kumar
88e1c28e7d test: advance against purchase invoice
(cherry picked from commit 90c84822d0)
2024-06-22 23:58:09 +00:00
ruthra kumar
259d7cde39 test: exc gain/loss booking on advances under asset/liability
(cherry picked from commit 827d67d02f)
2024-06-22 23:58:09 +00:00
ruthra kumar
6ebd4ba2cc refactor(test): simpler create_account helper method
(cherry picked from commit 475e0ddeee)
2024-06-22 23:58:09 +00:00
ruthra kumar
47071cec5d refactor: for advances uses the party account in references table
(cherry picked from commit 7dce6e03c7)
2024-06-22 23:58:09 +00:00
ruthra kumar
5d2f296ca8 refactor: convert amount to base currency for advances
(cherry picked from commit c9ede1ffbe)
2024-06-22 23:58:09 +00:00
ruthra kumar
199a64937b chore: remove validation on payment entry
(cherry picked from commit e7740033ca)
2024-06-22 23:58:09 +00:00
Sagar Vora
a535933a09 Merge pull request #41995 from frappe/mergify/bp/version-15-hotfix/pr-41993
perf: dont run queries unnecessarily, improved filters (backport #41993)
2024-06-22 21:51:27 +05:30
Sagar Vora
b59c91a341 perf: dont run queries unnecessarily, improved filters (#41993)
* perf: dont run queries unnecessarily, improved filters

* perf: dont run query if `in` filter is empty

(cherry picked from commit ac6d85aed6)
2024-06-22 15:58:00 +00:00
mergify[bot]
44c16713ba feat: default account head for operating cost (backport #41985) (#41987)
* feat: default account head for operating cost (#41985)

(cherry picked from commit fd7666a029)

# Conflicts:
#	erpnext/manufacturing/doctype/bom/bom.py
#	erpnext/setup/doctype/company/company.json

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

* chore: fix conflicts

---------

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-22 10:39:53 +05:30
Khushi Rawat
8d299d1495 Merge pull request #41989 from khushi8112/rename-number-of-depreciation-v15-hotfix
refactor: renamed number of depreciations booked to opening booked depreciations
2024-06-22 01:19:16 +05:30
Khushi Rawat
f9574366b5 chore: added nosemgrep for security checks 2024-06-22 01:01:03 +05:30
Khushi Rawat
7b5d5043c5 fix: reload asset when creating asset depreciation 2024-06-22 00:57:00 +05:30
Khushi Rawat
ca343f12d8 refactor: renamed number of depreciations booked to opening booked de… (#41515)
* refactor: renamed number of depreciations booked to opening booked depreciations

* feat: introduced new field for showing total number of booked depreciations
2024-06-21 19:53:46 +05:30
ruthra kumar
9515b96049 Merge pull request #41984 from frappe/mergify/bp/version-15-hotfix/pr-41981
fix: incorrect against_account upon reposting (backport #41981)
2024-06-21 19:10:33 +05:30
mergify[bot]
9945a90b3f fix: decimal issue in pick list (backport #41972) (#41982)
fix: decimal issue in pick list (#41972)

(cherry picked from commit 21adc7b63e)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-06-21 18:26:05 +05:30
ruthra kumar
a41577a1cd fix: incorrect against_account upon reposting
(cherry picked from commit 20c4098399)
2024-06-21 12:30:39 +00:00
ruthra kumar
657daf9a43 Merge pull request #41977 from frappe/mergify/bp/version-15-hotfix/pr-41956
fix: incorrect discount on other item (backport #41956)
2024-06-21 15:55:22 +05:30
ruthra kumar
77f4199e2a fix: incorrect discount on other item
When discount is applied on other item, don't update `discount_amount`
as the amount is calculated for current item

(cherry picked from commit 654764e398)
2024-06-21 10:23:42 +00:00
Raffael Meyer
7b322e7437 Merge pull request #41441 from frappe/mergify/bp/version-15-hotfix/pr-41384
fix(SO, DN): only show permitted actions (backport #41384)
2024-06-20 15:58:39 +02:00
barredterra
21802396ce fix: move condition for shipment 2024-06-20 14:32:01 +02:00
barredterra
9bad219f0a refactor: remove use of can_create for Payment Request (#41647)
(cherry picked from commit 47bc5691a1)
2024-06-20 14:28:24 +02:00
barredterra
ddbf9317a1 Merge remote-tracking branch 'upstream/version-15-hotfix' into mergify/bp/version-15-hotfix/pr-41384 2024-06-20 14:27:08 +02:00
mergify[bot]
48dc24b9bf fix: add string for translation (backport #41903) (#41963)
fix: add string for translation (#41903)

fix: add string for translation
(cherry picked from commit f28c692dca)

Co-authored-by: mahsem <137205921+mahsem@users.noreply.github.com>
2024-06-20 17:05:39 +05:30
mergify[bot]
c10b123a81 fix: do not show zero balance stock items in stock balance report (backport #41958) (#41961)
fix: do not show zero balance stock in stock balance

(cherry picked from commit 7f7b363d48)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2024-06-20 17:04:58 +05:30
mergify[bot]
ff8027b8eb Merge branch 'version-15-hotfix' into mergify/bp/version-15-hotfix/pr-41384 2024-06-17 17:46:28 +00:00
Nihantra C. Patel
d48a2c9f8e fix: correcting balance sheet calculation for zero liabilities and equity 2024-06-17 15:14:59 +05:30
Nihantra C. Patel
0bab609a6f fix: completed DC will not appear in a delivery trip 2024-06-17 15:09:31 +05:30
Nihantra C. Patel
a3444a07b7 fix: completed DC will not appear in a delivery trip (#41655)
* fix: completed DC will not appear in a delivery trip

* fix: completed DC will not appear in a delivery trip

(cherry picked from commit 4f0214d00e)

# Conflicts:
#	erpnext/stock/doctype/delivery_note/delivery_note.js
2024-06-03 11:57:39 +00:00
Nihantra C. Patel
2104d903aa fix: correcting balance sheet calculation for zero liabilities and equity (#41497)
* fix: correcting balance sheet calculation for zero liabilities and equity

* fix: correcting balance sheet calculation for zero liabilities and equity

(cherry picked from commit 3c3313594d)

# Conflicts:
#	erpnext/accounts/report/balance_sheet/balance_sheet.py
2024-06-03 10:36:56 +00:00
barredterra
cef6d0d74d fix(Delivery Note): only show permitted actions
(cherry picked from commit 418bdc1dcc)
2024-05-13 13:43:05 +00:00
barredterra
a0011c5b52 fix(Sales Order): only show permitted actions
(cherry picked from commit c29d955371)
2024-05-13 13:43:05 +00:00
164 changed files with 3761 additions and 1149 deletions

View File

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

View File

@@ -121,7 +121,8 @@
"label": "Account Type",
"oldfieldname": "account_type",
"oldfieldtype": "Select",
"options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary"
"options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary",
"search_index": 1
},
{
"description": "Rate at which this tax is applied",
@@ -190,7 +191,7 @@
"idx": 1,
"is_tree": 1,
"links": [],
"modified": "2023-07-20 18:18:44.405723",
"modified": "2024-06-27 16:23:04.444354",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account",
@@ -251,4 +252,4 @@
"sort_order": "ASC",
"states": [],
"track_changes": 1
}
}

View File

@@ -255,14 +255,16 @@ def get_accounting_dimensions(as_list=True, filters=None):
def get_checks_for_pl_and_bs_accounts():
dimensions = frappe.db.sql(
"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
WHERE p.name = c.parent""",
as_dict=1,
)
if frappe.flags.accounting_dimensions_details is None:
# nosemgrep
frappe.flags.accounting_dimensions_details = frappe.db.sql(
"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
WHERE p.name = c.parent""",
as_dict=1,
)
return dimensions
return frappe.flags.accounting_dimensions_details
def get_dimension_with_children(doctype, dimensions):

View File

@@ -78,6 +78,8 @@ class TestAccountingDimension(unittest.TestCase):
def tearDown(self):
disable_dimension()
frappe.flags.accounting_dimensions_details = None
frappe.flags.dimension_filter_map = None
def create_dimension():

View File

@@ -66,37 +66,41 @@ class AccountingDimensionFilter(Document):
def get_dimension_filter_map():
filters = frappe.db.sql(
"""
SELECT
a.applicable_on_account, d.dimension_value, p.accounting_dimension,
p.allow_or_restrict, a.is_mandatory
FROM
`tabApplicable On Account` a,
`tabAccounting Dimension Filter` p
LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name
WHERE
p.name = a.parent
AND p.disabled = 0
""",
as_dict=1,
)
dimension_filter_map = {}
for f in filters:
f.fieldname = scrub(f.accounting_dimension)
build_map(
dimension_filter_map,
f.fieldname,
f.applicable_on_account,
f.dimension_value,
f.allow_or_restrict,
f.is_mandatory,
if not frappe.flags.get("dimension_filter_map"):
# nosemgrep
filters = frappe.db.sql(
"""
SELECT
a.applicable_on_account, d.dimension_value, p.accounting_dimension,
p.allow_or_restrict, a.is_mandatory
FROM
`tabApplicable On Account` a, `tabAllowed Dimension` d,
`tabAccounting Dimension Filter` p
WHERE
p.name = a.parent
AND p.disabled = 0
AND p.name = d.parent
""",
as_dict=1,
)
return dimension_filter_map
dimension_filter_map = {}
for f in filters:
f.fieldname = scrub(f.accounting_dimension)
build_map(
dimension_filter_map,
f.fieldname,
f.applicable_on_account,
f.dimension_value,
f.allow_or_restrict,
f.is_mandatory,
)
frappe.flags.dimension_filter_map = dimension_filter_map
return frappe.flags.dimension_filter_map
def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):

View File

@@ -47,6 +47,8 @@ class TestAccountingDimensionFilter(unittest.TestCase):
def tearDown(self):
disable_dimension_filter()
disable_dimension()
frappe.flags.accounting_dimensions_details = None
frappe.flags.dimension_filter_map = None
for si in self.invoice_list:
si.load_from_db()

View File

@@ -55,6 +55,8 @@
"post_change_gl_entries",
"assets_tab",
"asset_settings_section",
"calculate_depr_using_total_days",
"column_break_gjcc",
"book_asset_depreciation_entry_automatically",
"closing_settings_tab",
"period_closing_settings_section",
@@ -71,7 +73,9 @@
"remarks_section",
"general_ledger_remarks_length",
"column_break_lvjk",
"receivable_payable_remarks_length"
"receivable_payable_remarks_length",
"payment_request_settings",
"create_pr_in_draft_status"
],
"fields": [
{
@@ -462,6 +466,29 @@
"fieldname": "enable_immutable_ledger",
"fieldtype": "Check",
"label": "Enable Immutable Ledger"
},
{
"fieldname": "column_break_gjcc",
"fieldtype": "Column Break"
},
{
"default": "0",
"description": "Enable this option to calculate daily depreciation by considering the total number of days in the entire depreciation period, (including leap years) while using daily pro-rata based depreciation",
"fieldname": "calculate_depr_using_total_days",
"fieldtype": "Check",
"label": "Calculate daily depreciation using total days in depreciation period"
},
{
"description": "Payment Request created from Sales Order or Purchase Order will be in Draft status. When disabled document will be in unsaved state.",
"fieldname": "payment_request_settings",
"fieldtype": "Tab Break",
"label": "Payment Request"
},
{
"default": "1",
"fieldname": "create_pr_in_draft_status",
"fieldtype": "Check",
"label": "Create in Draft Status"
}
],
"icon": "icon-cog",
@@ -469,7 +496,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2024-05-11 23:19:44.673975",
"modified": "2024-07-26 06:48:52.714630",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
@@ -498,4 +525,4 @@
"sort_order": "ASC",
"states": [],
"track_changes": 1
}
}

View File

@@ -33,7 +33,9 @@ class AccountsSettings(Document):
book_deferred_entries_based_on: DF.Literal["Days", "Months"]
book_deferred_entries_via_journal_entry: DF.Check
book_tax_discount_loss: DF.Check
calculate_depr_using_total_days: DF.Check
check_supplier_invoice_uniqueness: DF.Check
create_pr_in_draft_status: DF.Check
credit_controller: DF.Link | None
delete_linked_ledger_entries: DF.Check
determine_address_tax_category_from: DF.Literal["Billing Address", "Shipping Address"]

View File

@@ -120,52 +120,66 @@ frappe.ui.form.on("Bank Statement Import", {
},
show_import_status(frm) {
let import_log = JSON.parse(frm.doc.statement_import_log || "[]");
let successful_records = import_log.filter((log) => log.success);
let failed_records = import_log.filter((log) => !log.success);
if (successful_records.length === 0) return;
if (frm.doc.status == "Pending") return;
let message;
if (failed_records.length === 0) {
let message_args = [successful_records.length];
if (frm.doc.import_type === "Insert New Records") {
message =
successful_records.length > 1
? __("Successfully imported {0} records.", message_args)
: __("Successfully imported {0} record.", message_args);
} else {
message =
successful_records.length > 1
? __("Successfully updated {0} records.", message_args)
: __("Successfully updated {0} record.", message_args);
}
} else {
let message_args = [successful_records.length, import_log.length];
if (frm.doc.import_type === "Insert New Records") {
message =
successful_records.length > 1
? __(
"Successfully imported {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
message_args
)
: __(
"Successfully imported {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
message_args
);
} else {
message =
successful_records.length > 1
? __(
"Successfully updated {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
message_args
)
: __(
"Successfully updated {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
message_args
);
}
}
frm.dashboard.set_headline(message);
frappe.call({
method: "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.get_import_status",
args: {
docname: frm.doc.name,
},
callback: function (r) {
let successful_records = cint(r.message.success);
let failed_records = cint(r.message.failed);
let total_records = cint(r.message.total_records);
if (!total_records) {
return;
}
let message;
if (failed_records === 0) {
let message_args = [successful_records];
if (frm.doc.import_type === "Insert New Records") {
message =
successful_records > 1
? __("Successfully imported {0} records.", message_args)
: __("Successfully imported {0} record.", message_args);
} else {
message =
successful_records > 1
? __("Successfully updated {0} records.", message_args)
: __("Successfully updated {0} record.", message_args);
}
} else {
let message_args = [successful_records, total_records];
if (frm.doc.import_type === "Insert New Records") {
message =
successful_records > 1
? __(
"Successfully imported {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
message_args
)
: __(
"Successfully imported {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
message_args
);
} else {
message =
successful_records > 1
? __(
"Successfully updated {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.",
message_args
)
: __(
"Successfully updated {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.",
message_args
);
}
}
frm.dashboard.set_headline(message);
},
});
},
show_report_error_button(frm) {
@@ -287,7 +301,7 @@ frappe.ui.form.on("Bank Statement Import", {
// method: 'frappe.core.doctype.data_import.data_import.get_preview_from_template',
show_import_preview(frm, preview_data) {
let import_log = JSON.parse(frm.doc.statement_import_log || "[]");
let import_log = preview_data.import_log;
if (frm.import_preview && frm.import_preview.doctype === frm.doc.reference_doctype) {
frm.import_preview.preview_data = preview_data;
@@ -326,6 +340,15 @@ frappe.ui.form.on("Bank Statement Import", {
);
},
export_import_log(frm) {
open_url_post(
"/api/method/erpnext.accounts.doctype.bank_statement_import.bank_statement_import.download_import_log",
{
data_import_name: frm.doc.name,
}
);
},
show_import_warnings(frm, preview_data) {
let columns = preview_data.columns;
let warnings = JSON.parse(frm.doc.template_warnings || "[]");
@@ -401,49 +424,50 @@ frappe.ui.form.on("Bank Statement Import", {
frm.trigger("show_import_log");
},
show_import_log(frm) {
let import_log = JSON.parse(frm.doc.statement_import_log || "[]");
let logs = import_log;
frm.toggle_display("import_log", false);
frm.toggle_display("import_log_section", logs.length > 0);
render_import_log(frm) {
frappe.call({
method: "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.get_import_logs",
args: {
docname: frm.doc.name,
},
callback: function (r) {
let logs = r.message;
if (logs.length === 0) {
frm.get_field("import_log_preview").$wrapper.empty();
return;
}
if (logs.length === 0) return;
let rows = logs
.map((log) => {
let html = "";
if (log.success) {
if (frm.doc.import_type === "Insert New Records") {
html = __("Successfully imported {0}", [
`<span class="underline">${frappe.utils.get_form_link(
frm.doc.reference_doctype,
log.docname,
true
)}<span>`,
]);
} else {
html = __("Successfully updated {0}", [
`<span class="underline">${frappe.utils.get_form_link(
frm.doc.reference_doctype,
log.docname,
true
)}<span>`,
]);
}
} else {
let messages = log.messages
.map(JSON.parse)
.map((m) => {
let title = m.title ? `<strong>${m.title}</strong>` : "";
let message = m.message ? `<div>${m.message}</div>` : "";
return title + message;
})
.join("");
let id = frappe.dom.get_unique_id();
html = `${messages}
frm.toggle_display("import_log_section", true);
let rows = logs
.map((log) => {
let html = "";
if (log.success) {
if (frm.doc.import_type === "Insert New Records") {
html = __("Successfully imported {0}", [
`<span class="underline">${frappe.utils.get_form_link(
frm.doc.reference_doctype,
log.docname,
true
)}<span>`,
]);
} else {
html = __("Successfully updated {0}", [
`<span class="underline">${frappe.utils.get_form_link(
frm.doc.reference_doctype,
log.docname,
true
)}<span>`,
]);
}
} else {
let messages = JSON.parse(log.messages || "[]")
.map((m) => {
let title = m.title ? `<strong>${m.title}</strong>` : "";
let message = m.message ? `<div>${m.message}</div>` : "";
return title + message;
})
.join("");
let id = frappe.dom.get_unique_id();
html = `${messages}
<button class="btn btn-default btn-xs" type="button" data-toggle="collapse" data-target="#${id}" aria-expanded="false" aria-controls="${id}" style="margin-top: 15px;">
${__("Show Traceback")}
</button>
@@ -452,16 +476,16 @@ frappe.ui.form.on("Bank Statement Import", {
<pre>${log.exception}</pre>
</div>
</div>`;
}
let indicator_color = log.success ? "green" : "red";
let title = log.success ? __("Success") : __("Failure");
}
let indicator_color = log.success ? "green" : "red";
let title = log.success ? __("Success") : __("Failure");
if (frm.doc.show_failed_logs && log.success) {
return "";
}
if (frm.doc.show_failed_logs && log.success) {
return "";
}
return `<tr>
<td>${log.row_indexes.join(", ")}</td>
return `<tr>
<td>${JSON.parse(log.row_indexes).join(", ")}</td>
<td>
<div class="indicator ${indicator_color}">${title}</div>
</td>
@@ -469,16 +493,16 @@ frappe.ui.form.on("Bank Statement Import", {
${html}
</td>
</tr>`;
})
.join("");
})
.join("");
if (!rows && frm.doc.show_failed_logs) {
rows = `<tr><td class="text-center text-muted" colspan=3>
if (!rows && frm.doc.show_failed_logs) {
rows = `<tr><td class="text-center text-muted" colspan=3>
${__("No failed logs")}
</td></tr>`;
}
}
frm.get_field("import_log_preview").$wrapper.html(`
frm.get_field("import_log_preview").$wrapper.html(`
<table class="table table-bordered">
<tr class="text-muted">
<th width="10%">${__("Row Number")}</th>
@@ -488,5 +512,34 @@ frappe.ui.form.on("Bank Statement Import", {
${rows}
</table>
`);
},
});
},
show_import_log(frm) {
frm.toggle_display("import_log_section", false);
if (frm.is_new() || frm.import_in_progress) {
return;
}
frappe.call({
method: "frappe.client.get_count",
args: {
doctype: "Data Import Log",
filters: {
data_import: frm.doc.name,
},
},
callback: function (r) {
let count = r.message;
if (count < 5000) {
frm.trigger("render_import_log");
} else {
frm.toggle_display("import_log_section", false);
frm.add_custom_button(__("Export Import Log"), () => frm.trigger("export_import_log"));
}
},
});
},
});

View File

@@ -11,6 +11,8 @@
"bank_account",
"bank",
"column_break_4",
"custom_delimiters",
"delimiter_options",
"google_sheets_url",
"refresh_google_sheet",
"html_5",
@@ -24,7 +26,6 @@
"section_import_preview",
"import_preview",
"import_log_section",
"statement_import_log",
"show_failed_logs",
"import_log_preview",
"reference_doctype",
@@ -194,15 +195,23 @@
"fieldtype": "Column Break"
},
{
"fieldname": "statement_import_log",
"fieldtype": "Code",
"label": "Statement Import Log",
"options": "JSON"
"default": "0",
"fieldname": "custom_delimiters",
"fieldtype": "Check",
"label": "Custom delimiters"
},
{
"default": ",;\\t|",
"depends_on": "custom_delimiters",
"description": "If your CSV uses a different delimiter, add that character here, ensuring no spaces or additional characters are included.",
"fieldname": "delimiter_options",
"fieldtype": "Data",
"label": "Delimiter options"
}
],
"hide_toolbar": 1,
"links": [],
"modified": "2022-09-07 11:11:40.293317",
"modified": "2024-06-25 17:32:07.658250",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Statement Import",

View File

@@ -31,13 +31,14 @@ class BankStatementImport(DataImport):
bank: DF.Link | None
bank_account: DF.Link
company: DF.Link
custom_delimiters: DF.Check
delimiter_options: DF.Data | None
google_sheets_url: DF.Data | None
import_file: DF.Attach | None
import_type: DF.Literal["", "Insert New Records", "Update Existing Records"]
mute_emails: DF.Check
reference_doctype: DF.Link
show_failed_logs: DF.Check
statement_import_log: DF.Code | None
status: DF.Literal["Pending", "Success", "Partial Success", "Error"]
submit_after_import: DF.Check
template_options: DF.Code | None
@@ -120,6 +121,11 @@ def download_errored_template(data_import_name):
data_import.export_errored_rows()
@frappe.whitelist()
def download_import_log(data_import_name):
return frappe.get_doc("Bank Statement Import", data_import_name).download_import_log()
def parse_data_from_template(raw_data):
data = []
@@ -241,6 +247,47 @@ def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None):
return True
@frappe.whitelist()
def get_import_status(docname):
import_status = {}
data_import = frappe.get_doc("Bank Statement Import", docname)
import_status["status"] = data_import.status
logs = frappe.get_all(
"Data Import Log",
fields=["count(*) as count", "success"],
filters={"data_import": docname},
group_by="success",
)
total_payload_count = 0
for log in logs:
total_payload_count += log.get("count", 0)
if log.get("success"):
import_status["success"] = log.get("count")
else:
import_status["failed"] = log.get("count")
import_status["total_records"] = total_payload_count
return import_status
@frappe.whitelist()
def get_import_logs(docname: str):
frappe.has_permission("Bank Statement Import")
return frappe.get_all(
"Data Import Log",
fields=["success", "docname", "messages", "exception", "row_indexes"],
filters={"data_import": docname},
limit_page_length=5000,
order_by="log_index",
)
@frappe.whitelist()
def upload_bank_statement(**args):
args = frappe._dict(args)

View File

@@ -142,6 +142,8 @@ class Budget(Document):
def validate_expense_against_budget(args, expense_amount=0):
args = frappe._dict(args)
if not frappe.get_all("Budget", limit=1):
return
if args.get("company") and not args.fiscal_year:
args.fiscal_year = get_fiscal_year(args.get("posting_date"), company=args.get("company"))[0]
@@ -149,6 +151,9 @@ def validate_expense_against_budget(args, expense_amount=0):
"Company", args.get("company"), "exception_budget_approver_role"
)
if not frappe.get_cached_value("Budget", {"fiscal_year": args.fiscal_year, "company": args.company}): # nosec
return
if not args.account:
args.account = args.get("expense_account")
@@ -175,12 +180,12 @@ def validate_expense_against_budget(args, expense_amount=0):
if (
args.get(budget_against)
and args.account
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})
and (frappe.get_cached_value("Account", args.account, "root_type") == "Expense")
):
doctype = dimension.get("document_type")
if frappe.get_cached_value("DocType", doctype, "is_tree"):
lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"])
lft, rgt = frappe.get_cached_value(doctype, args.get(budget_against), ["lft", "rgt"])
condition = f"""and exists(select name from `tab{doctype}`
where lft<={lft} and rgt>={rgt} and name=b.{budget_against})""" # nosec
args.is_tree = True

View File

@@ -1,94 +1,42 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2016-05-16 11:54:09.286135",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"actions": [],
"creation": "2016-05-16 11:54:09.286135",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"account",
"budget_amount"
],
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"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,
"unique": 0
},
"fieldname": "account",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Account",
"options": "Account",
"reqd": 1,
"search_index": 1
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "budget_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Budget Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"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,
"unique": 0
"fieldname": "budget_amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Budget Amount",
"options": "Company:company:default_currency",
"reqd": 1
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-01-02 17:02:53.339420",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Budget Account",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
],
"istable": 1,
"links": [],
"modified": "2024-03-04 15:43:27.016947",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Budget Account",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}

View File

@@ -179,7 +179,8 @@
"fieldname": "voucher_detail_no",
"fieldtype": "Data",
"label": "Voucher Detail No",
"read_only": 1
"read_only": 1,
"search_index": 1
},
{
"fieldname": "project",
@@ -290,7 +291,7 @@
"idx": 1,
"in_create": 1,
"links": [],
"modified": "2023-12-18 15:38:14.006208",
"modified": "2024-07-02 14:31:51.496466",
"modified_by": "Administrator",
"module": "Accounts",
"name": "GL Entry",
@@ -322,7 +323,7 @@
],
"quick_entry": 1,
"search_fields": "voucher_no,account,posting_date,against_voucher",
"sort_field": "modified",
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -32,8 +32,6 @@ class GLEntry(Document):
account: DF.Link | None
account_currency: DF.Link | None
against: DF.Text | None
against_link: DF.DynamicLink | None
against_type: DF.Link | None
against_voucher: DF.DynamicLink | None
against_voucher_type: DF.Link | None
company: DF.Link | None
@@ -328,7 +326,7 @@ def update_outstanding_amt(
party_condition = ""
if against_voucher_type == "Sales Invoice":
party_account = frappe.db.get_value(against_voucher_type, against_voucher, "debit_to")
party_account = frappe.get_cached_value(against_voucher_type, against_voucher, "debit_to")
account_condition = f"and account in ({frappe.db.escape(account)}, {frappe.db.escape(party_account)})"
else:
account_condition = f" and account = {frappe.db.escape(account)}"
@@ -392,7 +390,9 @@ def update_outstanding_amt(
def validate_frozen_account(account, adv_adj=None):
frozen_account = frappe.get_cached_value("Account", account, "freeze_account")
if frozen_account == "Yes" and not adv_adj:
frozen_accounts_modifier = frappe.db.get_value("Accounts Settings", None, "frozen_accounts_modifier")
frozen_accounts_modifier = frappe.get_cached_value(
"Accounts Settings", None, "frozen_accounts_modifier"
)
if not frozen_accounts_modifier:
frappe.throw(_("Account {0} is frozen").format(account))

View File

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

View File

@@ -64,8 +64,7 @@
"stock_entry",
"subscription_section",
"auto_repeat",
"amended_from",
"repost_required"
"amended_from"
],
"fields": [
{
@@ -544,15 +543,6 @@
"label": "Is System Generated",
"no_copy": 1,
"read_only": 1
},
{
"default": "0",
"fieldname": "repost_required",
"fieldtype": "Check",
"hidden": 1,
"label": "Repost Required",
"print_hide": 1,
"read_only": 1
}
],
"icon": "fa fa-file-text",
@@ -567,7 +557,7 @@
"table_fieldname": "payment_entries"
}
],
"modified": "2023-11-23 12:11:04.128015",
"modified": "2024-07-18 15:32:29.413598",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",
@@ -618,4 +608,4 @@
"states": [],
"title_field": "title",
"track_changes": 1
}
}

View File

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

View File

@@ -454,12 +454,9 @@ class TestJournalEntry(unittest.TestCase):
# Change cost center for bank account - _Test Cost Center for BS Account
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
jv.accounts[1].cost_center = "_Test Cost Center for BS Account - _TC"
# Ledger reposted implicitly upon 'Update After Submit'
jv.save()
# Check if repost flag gets set on update after submit
self.assertTrue(jv.repost_required)
jv.repost_accounting_entries()
# Check GL entries after reposting
jv.load_from_db()
self.expected_gle[0]["cost_center"] = "_Test Cost Center for BS Account - _TC"
@@ -481,6 +478,43 @@ class TestJournalEntry(unittest.TestCase):
for field in self.fields:
self.assertEqual(self.expected_gle[i][field], gl_entries[i][field])
def test_negative_debit_and_credit_with_same_account_head(self):
from erpnext.accounts.general_ledger import process_gl_map
# Create JV with defaut cost center - _Test Cost Center
frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0)
jv = make_journal_entry("_Test Bank - _TC", "_Test Bank - _TC", 100 * -1, save=True)
jv.append(
"accounts",
{
"account": "_Test Cash - _TC",
"debit": 100 * -1,
"credit": 100 * -1,
"debit_in_account_currency": 100 * -1,
"credit_in_account_currency": 100 * -1,
"exchange_rate": 1,
},
)
jv.flags.ignore_validate = True
jv.save()
self.assertEqual(len(jv.accounts), 3)
gl_map = jv.build_gl_map()
for row in gl_map:
if row.account == "_Test Cash - _TC":
self.assertEqual(row.debit_in_account_currency, 100 * -1)
self.assertEqual(row.credit_in_account_currency, 100 * -1)
gl_map = process_gl_map(gl_map, False)
for row in gl_map:
if row.account == "_Test Cash - _TC":
self.assertEqual(row.debit_in_account_currency, 100)
self.assertEqual(row.credit_in_account_currency, 100)
def make_journal_entry(
account1,

View File

@@ -82,7 +82,6 @@ class PaymentEntry(AccountsController):
self.set_exchange_rate()
self.validate_mandatory()
self.validate_reference_documents()
self.set_tax_withholding()
self.set_amounts()
self.validate_amounts()
self.apply_taxes()
@@ -96,6 +95,7 @@ class PaymentEntry(AccountsController):
self.validate_allocated_amount()
self.validate_paid_invoices()
self.ensure_supplier_is_not_blocked()
self.set_tax_withholding()
self.set_status()
self.set_total_in_words()
@@ -756,9 +756,7 @@ class PaymentEntry(AccountsController):
if not self.apply_tax_withholding_amount:
return
order_amount = self.get_order_net_total()
net_total = flt(order_amount) + flt(self.unallocated_amount)
net_total = self.calculate_tax_withholding_net_total()
# Adding args as purchase invoice to get TDS amount
args = frappe._dict(
@@ -802,7 +800,26 @@ class PaymentEntry(AccountsController):
for d in to_remove:
self.remove(d)
def get_order_net_total(self):
def calculate_tax_withholding_net_total(self):
net_total = 0
order_details = self.get_order_wise_tax_withholding_net_total()
for d in self.references:
tax_withholding_net_total = order_details.get(d.reference_name)
if not tax_withholding_net_total:
continue
net_taxable_outstanding = max(
0, d.outstanding_amount - (d.total_amount - tax_withholding_net_total)
)
net_total += min(net_taxable_outstanding, d.allocated_amount)
net_total += self.unallocated_amount
return net_total
def get_order_wise_tax_withholding_net_total(self):
if self.party_type == "Supplier":
doctype = "Purchase Order"
else:
@@ -810,12 +827,15 @@ class PaymentEntry(AccountsController):
docnames = [d.reference_name for d in self.references if d.reference_doctype == doctype]
tax_withholding_net_total = frappe.db.get_value(
doctype, {"name": ["in", docnames]}, ["sum(base_tax_withholding_net_total)"]
return frappe._dict(
frappe.db.get_all(
doctype,
filters={"name": ["in", docnames]},
fields=["name", "base_tax_withholding_net_total"],
as_list=True,
)
)
return tax_withholding_net_total
def apply_taxes(self):
self.initialize_taxes()
self.determine_exclusive_rate()
@@ -1217,13 +1237,21 @@ class PaymentEntry(AccountsController):
if reference.reference_doctype == "Sales Invoice":
return "credit", reference.account
if reference.reference_doctype == "Purchase Invoice":
return "debit", reference.account
if reference.reference_doctype == "Payment Entry":
# reference.account_type and reference.payment_type is only available for Reverse payments
if reference.account_type == "Receivable" and reference.payment_type == "Pay":
return "credit", self.party_account
else:
return "debit", self.party_account
return "debit", reference.account
if reference.reference_doctype == "Journal Entry":
if self.party_type == "Customer" and self.payment_type == "Receive":
return "credit", reference.account
else:
return "debit", reference.account
def add_advance_gl_for_reference(self, gl_entries, invoice):
args_dict = {

View File

@@ -82,7 +82,7 @@ class TestPaymentEntry(FrappeTestCase):
expected_gle = dict(
(d[0], d)
for d in [["_Test Receivable USD - _TC", 0, 5500, so.name], ["Cash - _TC", 5500.0, 0, None]]
for d in [["_Test Receivable USD - _TC", 0, 5500, so.name], [pe.paid_to, 5500.0, 0, None]]
)
self.validate_gl_entries(pe.name, expected_gle)

View File

@@ -161,11 +161,12 @@ class PaymentLedgerEntry(Document):
def on_update(self):
adv_adj = self.flags.adv_adj
if not self.flags.from_repost:
self.validate_account_details()
self.validate_dimensions_for_pl_and_bs()
self.validate_allowed_dimensions()
validate_balance_type(self.account, adv_adj)
validate_frozen_account(self.account, adv_adj)
if not self.delinked:
self.validate_account_details()
self.validate_dimensions_for_pl_and_bs()
self.validate_allowed_dimensions()
validate_balance_type(self.account, adv_adj)
# update outstanding amount
if (

View File

@@ -509,7 +509,11 @@ class TestPaymentLedgerEntry(FrappeTestCase):
@change_settings(
"Accounts Settings",
{"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1},
{
"unlink_payment_on_cancellation_of_invoice": 1,
"delete_linked_ledger_entries": 1,
"unlink_advance_payment_on_cancelation_of_order": 1,
},
)
def test_advance_payment_unlink_on_order_cancellation(self):
transaction_date = nowdate()

View File

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

View File

@@ -267,6 +267,7 @@ class PaymentReconciliation(Document):
conditions.append(doc.docstatus == 1)
conditions.append(doc[frappe.scrub(self.party_type)] == self.party)
conditions.append(doc.is_return == 1)
conditions.append(doc.outstanding_amount != 0)
if self.payment_name:
conditions.append(doc.name.like(f"%{self.payment_name}%"))

View File

@@ -109,6 +109,14 @@ class TestPaymentReconciliation(FrappeTestCase):
"account_currency": "INR",
"account_type": "Payable",
},
# 'Receivable' account for capturing advance received, under 'Liabilities' group
{
"attribute": "advance_receivable_account",
"account_name": "Advance Received",
"parent_account": "Current Liabilities - _PR",
"account_currency": "INR",
"account_type": "Receivable",
},
]
for x in accounts:
@@ -1574,6 +1582,269 @@ class TestPaymentReconciliation(FrappeTestCase):
)
self.assertEqual(len(pl_entries), 3)
def test_advance_payment_reconciliation_against_journal_for_customer(self):
frappe.db.set_value(
"Company",
self.company,
{
"book_advance_payments_in_separate_party_account": 1,
"default_advance_received_account": self.advance_receivable_account,
"reconcile_on_advance_payment_date": 0,
},
)
amount = 200.0
je = self.create_journal_entry(self.debit_to, self.bank, amount)
je.accounts[0].cost_center = self.main_cc.name
je.accounts[0].party_type = "Customer"
je.accounts[0].party = self.customer
je.accounts[1].cost_center = self.main_cc.name
je = je.save().submit()
pe = self.create_payment_entry(amount=amount).save().submit()
pr = self.create_payment_reconciliation()
pr.default_advance_account = self.advance_receivable_account
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 1)
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}))
pr.reconcile()
# Assert Ledger Entries
gl_entries = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": pe.name, "is_cancelled": 0},
)
self.assertEqual(len(gl_entries), 4)
pl_entries = frappe.db.get_all(
"Payment Ledger Entry",
filters={"voucher_no": pe.name, "delinked": 0},
)
self.assertEqual(len(pl_entries), 3)
gl_entries = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": pe.name, "is_cancelled": 0},
fields=["account", "voucher_no", "against_voucher", "debit", "credit"],
order_by="account, against_voucher, debit",
)
expected_gle = [
{
"account": self.advance_receivable_account,
"voucher_no": pe.name,
"against_voucher": pe.name,
"debit": 0.0,
"credit": amount,
},
{
"account": self.advance_receivable_account,
"voucher_no": pe.name,
"against_voucher": pe.name,
"debit": amount,
"credit": 0.0,
},
{
"account": self.debit_to,
"voucher_no": pe.name,
"against_voucher": je.name,
"debit": 0.0,
"credit": amount,
},
{
"account": self.bank,
"voucher_no": pe.name,
"against_voucher": None,
"debit": amount,
"credit": 0.0,
},
]
self.assertEqual(gl_entries, expected_gle)
pl_entries = frappe.db.get_all(
"Payment Ledger Entry",
filters={"voucher_no": pe.name},
fields=["account", "voucher_no", "against_voucher_no", "amount"],
order_by="account, against_voucher_no, amount",
)
expected_ple = [
{
"account": self.advance_receivable_account,
"voucher_no": pe.name,
"against_voucher_no": pe.name,
"amount": -amount,
},
{
"account": self.advance_receivable_account,
"voucher_no": pe.name,
"against_voucher_no": pe.name,
"amount": amount,
},
{
"account": self.debit_to,
"voucher_no": pe.name,
"against_voucher_no": je.name,
"amount": -amount,
},
]
self.assertEqual(pl_entries, expected_ple)
def test_advance_payment_reconciliation_against_journal_for_supplier(self):
self.supplier = make_supplier("_Test Supplier")
frappe.db.set_value(
"Company",
self.company,
{
"book_advance_payments_in_separate_party_account": 1,
"default_advance_paid_account": self.advance_payable_account,
"reconcile_on_advance_payment_date": 0,
},
)
amount = 200.0
je = self.create_journal_entry(self.creditors, self.bank, -amount)
je.accounts[0].cost_center = self.main_cc.name
je.accounts[0].party_type = "Supplier"
je.accounts[0].party = self.supplier
je.accounts[1].cost_center = self.main_cc.name
je = je.save().submit()
pe = self.create_payment_entry(amount=amount)
pe.payment_type = "Pay"
pe.party_type = "Supplier"
pe.paid_from = self.bank
pe.paid_to = self.creditors
pe.party = self.supplier
pe.save().submit()
pr = self.create_payment_reconciliation(party_is_customer=False)
pr.default_advance_account = self.advance_payable_account
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 1)
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}))
pr.reconcile()
# Assert Ledger Entries
gl_entries = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": pe.name, "is_cancelled": 0},
)
self.assertEqual(len(gl_entries), 4)
pl_entries = frappe.db.get_all(
"Payment Ledger Entry",
filters={"voucher_no": pe.name, "delinked": 0},
)
self.assertEqual(len(pl_entries), 3)
gl_entries = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": pe.name, "is_cancelled": 0},
fields=["account", "voucher_no", "against_voucher", "debit", "credit"],
order_by="account, against_voucher, debit",
)
expected_gle = [
{
"account": self.advance_payable_account,
"voucher_no": pe.name,
"against_voucher": pe.name,
"debit": 0.0,
"credit": amount,
},
{
"account": self.advance_payable_account,
"voucher_no": pe.name,
"against_voucher": pe.name,
"debit": amount,
"credit": 0.0,
},
{
"account": self.creditors,
"voucher_no": pe.name,
"against_voucher": je.name,
"debit": amount,
"credit": 0.0,
},
{
"account": self.bank,
"voucher_no": pe.name,
"against_voucher": None,
"debit": 0.0,
"credit": amount,
},
]
self.assertEqual(gl_entries, expected_gle)
pl_entries = frappe.db.get_all(
"Payment Ledger Entry",
filters={"voucher_no": pe.name},
fields=["account", "voucher_no", "against_voucher_no", "amount"],
order_by="account, against_voucher_no, amount",
)
expected_ple = [
{
"account": self.advance_payable_account,
"voucher_no": pe.name,
"against_voucher_no": pe.name,
"amount": -amount,
},
{
"account": self.advance_payable_account,
"voucher_no": pe.name,
"against_voucher_no": pe.name,
"amount": amount,
},
{
"account": self.creditors,
"voucher_no": pe.name,
"against_voucher_no": je.name,
"amount": -amount,
},
]
self.assertEqual(pl_entries, expected_ple)
def test_cr_note_payment_limit_filter(self):
transaction_date = nowdate()
amount = 100
for _ in range(6):
self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
cr_note = self.create_sales_invoice(
qty=-1, rate=amount, posting_date=transaction_date, do_not_save=True, do_not_submit=True
)
cr_note.is_return = 1
cr_note = cr_note.save().submit()
pr = self.create_payment_reconciliation()
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 6)
self.assertEqual(len(pr.payments), 6)
invoices = [x.as_dict() for x in pr.get("invoices")]
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.reconcile()
pr.get_unreconciled_entries()
self.assertEqual(pr.get("invoices"), [])
self.assertEqual(pr.get("payments"), [])
self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
cr_note = self.create_sales_invoice(
qty=-1, rate=amount, posting_date=transaction_date, do_not_save=True, do_not_submit=True
)
cr_note.is_return = 1
cr_note = cr_note.save().submit()
# Limit should not affect in fetching the unallocated cr_note
pr.invoice_limit = 5
pr.payment_limit = 5
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 1)
def make_customer(customer_name, currency=None):
if not frappe.db.exists("Customer", customer_name):

View File

@@ -144,6 +144,7 @@
"fieldname": "grand_total",
"fieldtype": "Currency",
"label": "Amount",
"non_negative": 1,
"options": "currency"
},
{
@@ -395,7 +396,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-09-27 09:51:42.277638",
"modified": "2024-06-20 13:54:55.245774",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Request",
@@ -433,4 +434,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -500,7 +500,8 @@ def make_payment_request(**args):
if args.order_type == "Shopping Cart" or args.mute_email:
pr.flags.mute_email = True
pr.insert(ignore_permissions=True)
if frappe.db.get_single_value("Accounts Settings", "create_pr_in_draft_status", cache=True):
pr.insert(ignore_permissions=True)
if args.submit_doc:
pr.submit()

View File

@@ -136,18 +136,28 @@ class PeriodClosingVoucher(AccountsController):
def check_if_previous_year_closed(self):
last_year_closing = add_days(self.year_start_date, -1)
previous_fiscal_year = get_fiscal_year(last_year_closing, company=self.company, boolean=True)
if not previous_fiscal_year:
return
if previous_fiscal_year and not frappe.db.exists(
previous_fiscal_year_start_date = previous_fiscal_year[0][1]
if not frappe.db.exists(
"GL Entry",
{"posting_date": ("<=", last_year_closing), "company": self.company, "is_cancelled": 0},
{
"posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]),
"company": self.company,
"is_cancelled": 0,
},
):
return
if previous_fiscal_year and not frappe.db.exists(
if not frappe.db.exists(
"Period Closing Voucher",
{"posting_date": ("<=", last_year_closing), "docstatus": 1, "company": self.company},
{
"posting_date": ("between", [previous_fiscal_year_start_date, last_year_closing]),
"docstatus": 1,
"company": self.company,
},
):
frappe.throw(_("Previous Year is not closed, please close it first"))

View File

@@ -17,6 +17,10 @@ from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_inv
from erpnext.accounts.doctype.pos_opening_entry.test_pos_opening_entry import create_opening_entry
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
from erpnext.selling.page.point_of_sale.point_of_sale import get_items
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.serial_and_batch_bundle.test_serial_and_batch_bundle import (
get_batch_from_bundle,
)
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
@@ -179,6 +183,94 @@ class TestPOSClosingEntry(unittest.TestCase):
accounting_dimension_department.save()
disable_dimension()
def test_merging_into_sales_invoice_for_batched_item(self):
frappe.flags.print_message = False
from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import (
init_user_and_profile,
)
from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import (
consolidate_pos_invoices,
)
from erpnext.stock.doctype.batch.batch import get_batch_qty
frappe.db.sql("delete from `tabPOS Invoice`")
item_doc = make_item(
"_Test Item With Batch FOR POS Merge Test",
properties={
"is_stock_item": 1,
"has_batch_no": 1,
"batch_number_series": "BATCH-PM-POS-MERGE-.####",
"create_new_batch": 1,
},
)
item_code = item_doc.name
se = make_stock_entry(
target="_Test Warehouse - _TC",
item_code=item_code,
qty=10,
basic_rate=100,
use_serial_batch_fields=0,
)
batch_no = get_batch_from_bundle(se.items[0].serial_and_batch_bundle)
test_user, pos_profile = init_user_and_profile()
opening_entry = create_opening_entry(pos_profile, test_user.name)
pos_inv = create_pos_invoice(
item_code=item_code, qty=5, rate=300, use_serial_batch_fields=1, batch_no=batch_no
)
pos_inv2 = create_pos_invoice(
item_code=item_code, qty=5, rate=300, use_serial_batch_fields=1, batch_no=batch_no
)
batch_qty = frappe.db.get_value("Batch", batch_no, "batch_qty")
self.assertEqual(batch_qty, 10)
batch_qty_with_pos = get_batch_qty(batch_no, "_Test Warehouse - _TC", item_code)
self.assertEqual(batch_qty_with_pos, 0.0)
pcv_doc = make_closing_entry_from_opening(opening_entry)
pcv_doc.submit()
piv_merge = frappe.db.get_value("POS Invoice Merge Log", {"pos_closing_entry": pcv_doc.name}, "name")
self.assertTrue(piv_merge)
piv_merge_doc = frappe.get_doc("POS Invoice Merge Log", piv_merge)
self.assertTrue(piv_merge_doc.pos_invoices[0].pos_invoice)
self.assertTrue(piv_merge_doc.pos_invoices[1].pos_invoice)
pos_inv.load_from_db()
self.assertTrue(pos_inv.consolidated_invoice)
pos_inv2.load_from_db()
self.assertTrue(pos_inv2.consolidated_invoice)
batch_qty = frappe.db.get_value("Batch", batch_no, "batch_qty")
self.assertEqual(batch_qty, 0.0)
batch_qty_with_pos = get_batch_qty(batch_no, "_Test Warehouse - _TC", item_code)
self.assertEqual(batch_qty_with_pos, 0.0)
frappe.flags.print_message = True
pcv_doc.reload()
pcv_doc.cancel()
batch_qty = frappe.db.get_value("Batch", batch_no, "batch_qty")
self.assertEqual(batch_qty, 10)
batch_qty_with_pos = get_batch_qty(batch_no, "_Test Warehouse - _TC", item_code)
self.assertEqual(batch_qty_with_pos, 0.0)
pos_inv.reload()
pos_inv2.reload()
pos_inv.cancel()
pos_inv2.cancel()
batch_qty_with_pos = get_batch_qty(batch_no, "_Test Warehouse - _TC", item_code)
self.assertEqual(batch_qty_with_pos, 10.0)
def init_user_and_profile(**args):
user = "test@example.com"

View File

@@ -229,7 +229,9 @@ class POSInvoice(SalesInvoice):
self.check_phone_payments()
self.set_status(update=True)
self.make_bundle_for_sales_purchase_return()
self.submit_serial_batch_bundle()
for table_name in ["items", "packed_items"]:
self.make_bundle_using_old_serial_batch_fields(table_name)
self.submit_serial_batch_bundle(table_name)
if self.coupon_code:
from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count
@@ -283,10 +285,11 @@ class POSInvoice(SalesInvoice):
{"is_cancelled": 1, "voucher_no": ""},
)
frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle).cancel()
row.db_set("serial_and_batch_bundle", None)
def submit_serial_batch_bundle(self):
for item in self.items:
def submit_serial_batch_bundle(self, table_name):
for item in self.get(table_name):
if item.serial_and_batch_bundle:
doc = frappe.get_doc("Serial and Batch Bundle", item.serial_and_batch_bundle)
@@ -355,10 +358,16 @@ class POSInvoice(SalesInvoice):
error_msg = []
for d in self.get("items"):
error_msg = ""
if d.get("has_serial_no") and not d.serial_and_batch_bundle:
if d.get("has_serial_no") and (
(not d.use_serial_batch_fields and not d.serial_and_batch_bundle)
or (d.use_serial_batch_fields and not d.serial_no)
):
error_msg = f"Row #{d.idx}: Please select Serial No. for item {bold(d.item_code)}"
elif d.get("has_batch_no") and not d.serial_and_batch_bundle:
elif d.get("has_batch_no") and (
(not d.use_serial_batch_fields and not d.serial_and_batch_bundle)
or (d.use_serial_batch_fields and not d.batch_no)
):
error_msg = f"Row #{d.idx}: Please select Batch No. for item {bold(d.item_code)}"
if error_msg:

View File

@@ -780,8 +780,6 @@ class TestPOSInvoice(unittest.TestCase):
pos_inv1.submit()
pos_inv1.reload()
self.assertFalse(pos_inv1.items[0].serial_and_batch_bundle)
batches = get_auto_batch_nos(
frappe._dict({"item_code": "_BATCH ITEM Test For Reserve", "warehouse": "_Test Warehouse - _TC"})
)
@@ -957,7 +955,7 @@ def create_pos_invoice(**args):
pos_inv.set_missing_values()
bundle_id = None
if args.get("batch_no") or args.get("serial_no"):
if not args.use_serial_batch_fields and (args.get("batch_no") or args.get("serial_no")):
type_of_transaction = args.type_of_transaction or "Outward"
if pos_inv.is_return:
@@ -998,6 +996,9 @@ def create_pos_invoice(**args):
"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
"cost_center": args.cost_center or "_Test Cost Center - _TC",
"serial_and_batch_bundle": bundle_id,
"use_serial_batch_fields": args.use_serial_batch_fields,
"serial_no": args.serial_no if args.use_serial_batch_fields else None,
"batch_no": args.batch_no if args.use_serial_batch_fields else None,
}
# append in pos invoice items without item_code by checking flag without_item_code
if args.without_item_code:
@@ -1023,6 +1024,8 @@ def create_pos_invoice(**args):
pos_inv.insert()
if not args.do_not_submit:
pos_inv.submit()
if args.use_serial_batch_fields:
pos_inv.reload()
else:
pos_inv.payment_schedule = []
else:

View File

@@ -634,7 +634,6 @@
"depends_on": "eval:doc.use_serial_batch_fields === 1",
"fieldname": "batch_no",
"fieldtype": "Link",
"hidden": 1,
"label": "Batch No",
"options": "Batch",
"print_hide": 1
@@ -655,7 +654,6 @@
"depends_on": "eval:doc.use_serial_batch_fields === 1",
"fieldname": "serial_no",
"fieldtype": "Text",
"hidden": 1,
"in_list_view": 1,
"label": "Serial No",
"oldfieldname": "serial_no",
@@ -827,7 +825,7 @@
"read_only": 1
},
{
"depends_on": "eval:doc.use_serial_batch_fields === 1",
"depends_on": "eval:doc.use_serial_batch_fields === 0 || doc.serial_and_batch_bundle",
"fieldname": "serial_and_batch_bundle",
"fieldtype": "Link",
"label": "Serial and Batch Bundle",
@@ -853,7 +851,7 @@
],
"istable": 1,
"links": [],
"modified": "2024-02-25 15:50:17.140269",
"modified": "2024-05-07 15:56:53.343317",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice Item",
@@ -863,4 +861,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -131,6 +131,7 @@ class POSInvoiceMergeLog(Document):
pos_invoice_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
self.update_pos_invoices(pos_invoice_docs)
self.serial_and_batch_bundle_reference_for_pos_invoice()
self.cancel_linked_invoices()
def process_merging_into_sales_invoice(self, data):
@@ -191,6 +192,7 @@ class POSInvoiceMergeLog(Document):
for i in items:
if (
i.item_code == item.item_code
and not i.serial_and_batch_bundle
and not i.serial_no
and not i.batch_no
and i.uom == item.uom
@@ -312,6 +314,12 @@ class POSInvoiceMergeLog(Document):
doc.set_status(update=True)
doc.save()
def serial_and_batch_bundle_reference_for_pos_invoice(self):
for d in self.pos_invoices:
pos_invoice = frappe.get_doc("POS Invoice", d.pos_invoice)
for table_name in ["items", "packed_items"]:
pos_invoice.set_serial_and_batch_bundle(table_name)
def cancel_linked_invoices(self):
for si_name in [self.consolidated_invoice, self.consolidated_credit_note]:
if not si_name:

View File

@@ -139,6 +139,7 @@ class PricingRule(Document):
self.validate_price_list_with_currency()
self.validate_dates()
self.validate_condition()
self.validate_mixed_with_recursion()
if not self.margin_type:
self.margin_rate_or_amount = 0.0
@@ -308,6 +309,10 @@ class PricingRule(Document):
):
frappe.throw(_("Invalid condition expression"))
def validate_mixed_with_recursion(self):
if self.mixed_conditions and self.is_recursive:
frappe.throw(_("Recursive Discounts with Mixed condition is not supported by the system"))
# --------------------------------------------------------------------------------

View File

@@ -1299,6 +1299,18 @@ class TestPricingRule(unittest.TestCase):
item_group_rule.delete()
item_code_rule.delete()
def test_validation_on_mixed_condition_with_recursion(self):
pricing_rule = make_pricing_rule(
discount_percentage=10,
selling=1,
priority=2,
min_qty=4,
title="_Test Pricing Rule with Min Qty - 2",
)
pricing_rule.mixed_conditions = True
pricing_rule.is_recursive = True
self.assertRaises(frappe.ValidationError, pricing_rule.save)
test_dependencies = ["Campaign"]

View File

@@ -146,6 +146,7 @@ class PromotionalScheme(Document):
self.validate_applicable_for()
self.validate_pricing_rules()
self.validate_mixed_with_recursion()
def validate_applicable_for(self):
if self.applicable_for:
@@ -163,7 +164,7 @@ class PromotionalScheme(Document):
docnames = []
# If user has changed applicable for
if self._doc_before_save.applicable_for == self.applicable_for:
if self.get_doc_before_save() and self.get_doc_before_save().applicable_for == self.applicable_for:
return
docnames = frappe.get_all("Pricing Rule", filters={"promotional_scheme": self.name})
@@ -177,6 +178,7 @@ class PromotionalScheme(Document):
frappe.delete_doc("Pricing Rule", docname.name)
def on_update(self):
self.validate()
pricing_rules = (
frappe.get_all(
"Pricing Rule",
@@ -188,6 +190,15 @@ class PromotionalScheme(Document):
)
self.update_pricing_rules(pricing_rules)
def validate_mixed_with_recursion(self):
if self.mixed_conditions:
if self.product_discount_slabs:
for slab in self.product_discount_slabs:
if slab.is_recursive:
frappe.throw(
_("Recursive Discounts with Mixed condition is not supported by the system")
)
def update_pricing_rules(self, pricing_rules):
rules = {}
count = 0

View File

@@ -129,6 +129,25 @@ class TestPromotionalScheme(unittest.TestCase):
[pr.min_qty, pr.free_item, pr.free_qty, pr.recurse_for], [12, "_Test Item 2", 1, 12]
)
def test_validation_on_recurse_with_mixed_condition(self):
ps = make_promotional_scheme()
ps.set("price_discount_slabs", [])
ps.set(
"product_discount_slabs",
[
{
"rule_description": "12+1",
"min_qty": 12,
"free_item": "_Test Item 2",
"free_qty": 1,
"is_recursive": 1,
"recurse_for": 12,
}
],
)
ps.mixed_conditions = True
self.assertRaises(frappe.ValidationError, ps.save)
def make_promotional_scheme(**args):
args = frappe._dict(args)

View File

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

View File

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

View File

@@ -159,7 +159,6 @@ class PurchaseInvoice(BuyingController):
rejected_warehouse: DF.Link | None
release_date: DF.Date | None
remarks: DF.SmallText | None
repost_required: DF.Check
represents_company: DF.Link | None
return_against: DF.Link | None
rounded_total: DF.Currency
@@ -549,6 +548,21 @@ class PurchaseInvoice(BuyingController):
item.expense_account = stock_not_billed_account
elif item.is_fixed_asset:
account = None
if not item.pr_detail and item.po_detail:
receipt_item = frappe.get_cached_value(
"Purchase Receipt Item",
{
"purchase_order": item.purchase_order,
"purchase_order_item": item.po_detail,
"docstatus": 1,
},
["name", "parent"],
as_dict=1,
)
if receipt_item:
item.pr_detail = receipt_item.name
item.purchase_receipt = receipt_item.parent
if item.pr_detail:
if not self.asset_received_but_not_billed:
self.asset_received_but_not_billed = self.get_company_default(
@@ -781,27 +795,25 @@ class PurchaseInvoice(BuyingController):
self.process_common_party_accounting()
def on_update_after_submit(self):
if hasattr(self, "repost_required"):
fields_to_check = [
"cash_bank_account",
"write_off_account",
"unrealized_profit_loss_account",
"is_opening",
]
child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.db_set("repost_required", self.needs_repost)
fields_to_check = [
"cash_bank_account",
"write_off_account",
"unrealized_profit_loss_account",
"is_opening",
]
child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.repost_accounting_entries()
def make_gl_entries(self, gl_entries=None, from_repost=False):
if not gl_entries:
gl_entries = self.get_gl_entries()
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
if self.docstatus == 1:
if not gl_entries:
gl_entries = self.get_gl_entries()
if gl_entries:
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
if self.docstatus == 1:
if gl_entries:
make_gl_entries(
gl_entries,
update_outstanding=update_outstanding,
@@ -809,32 +821,43 @@ class PurchaseInvoice(BuyingController):
from_repost=from_repost,
)
self.make_exchange_gain_loss_journal()
elif self.docstatus == 2:
provisional_entries = [a for a in gl_entries if a.voucher_type == "Purchase Receipt"]
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if provisional_entries:
for entry in provisional_entries:
frappe.db.set_value(
"GL Entry",
{
"voucher_type": "Purchase Receipt",
"voucher_detail_no": entry.voucher_detail_no,
},
"is_cancelled",
1,
)
if update_outstanding == "No":
update_outstanding_amt(
self.credit_to,
"Supplier",
self.supplier,
self.doctype,
self.return_against if cint(self.is_return) and self.return_against else self.name,
)
elif self.docstatus == 2 and cint(self.update_stock) and self.auto_accounting_for_stock:
elif self.docstatus == 2:
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
self.cancel_provisional_entries()
self.update_supplier_outstanding(update_outstanding)
def cancel_provisional_entries(self):
rows = set()
purchase_receipts = set()
for d in self.items:
if d.purchase_receipt:
purchase_receipts.add(d.purchase_receipt)
rows.add(d.name)
if rows:
# cancel gl entries
gle = qb.DocType("GL Entry")
gle_update_query = (
qb.update(gle)
.set(gle.is_cancelled, 1)
.where(
(gle.voucher_type == "Purchase Receipt")
& (gle.voucher_no.isin(purchase_receipts))
& (gle.voucher_detail_no.isin(rows))
)
)
gle_update_query.run()
def update_supplier_outstanding(self, update_outstanding):
if update_outstanding == "No":
update_outstanding_amt(
self.credit_to,
"Supplier",
self.supplier,
self.doctype,
self.return_against if cint(self.is_return) and self.return_against else self.name,
)
def get_gl_entries(self, warehouse_account=None):
self.auto_accounting_for_stock = erpnext.is_perpetual_inventory_enabled(self.company)
@@ -947,8 +970,8 @@ class PurchaseInvoice(BuyingController):
"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
)
)
purchase_receipt_doc_map = {}
if provisional_accounting_for_non_stock_items:
self.get_provisional_accounts()
for item in self.get("items"):
if flt(item.base_net_amount):
@@ -1087,49 +1110,7 @@ class PurchaseInvoice(BuyingController):
dummy, amount = self.get_amount_and_base_amount(item, None)
if provisional_accounting_for_non_stock_items:
if item.purchase_receipt:
provisional_account, pr_qty, pr_base_rate, pr_rate = frappe.get_cached_value(
"Purchase Receipt Item",
item.pr_detail,
["provisional_expense_account", "qty", "base_rate", "rate"],
)
provisional_account = provisional_account or self.get_company_default(
"default_provisional_account"
)
purchase_receipt_doc = purchase_receipt_doc_map.get(item.purchase_receipt)
if not purchase_receipt_doc:
purchase_receipt_doc = frappe.get_doc(
"Purchase Receipt", item.purchase_receipt
)
purchase_receipt_doc_map[item.purchase_receipt] = purchase_receipt_doc
# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
expense_booked_in_pr = frappe.db.get_value(
"GL Entry",
{
"is_cancelled": 0,
"voucher_type": "Purchase Receipt",
"voucher_no": item.purchase_receipt,
"voucher_detail_no": item.pr_detail,
"account": provisional_account,
},
"name",
)
if expense_booked_in_pr:
# Intentionally passing purchase invoice item to handle partial billing
purchase_receipt_doc.add_provisional_gl_entry(
item,
gl_entries,
self.posting_date,
provisional_account,
reverse=1,
item_amount=(
(min(item.qty, pr_qty) * pr_rate)
* purchase_receipt_doc.get("conversion_rate")
),
)
self.make_provisional_gl_entry(gl_entries, item)
if not self.is_internal_transfer():
gl_entries.append(
@@ -1225,6 +1206,59 @@ class PurchaseInvoice(BuyingController):
if item.is_fixed_asset and item.landed_cost_voucher_amount:
self.update_gross_purchase_amount_for_linked_assets(item)
def get_provisional_accounts(self):
self.provisional_accounts = frappe._dict()
linked_purchase_receipts = set([d.purchase_receipt for d in self.items if d.purchase_receipt])
pr_items = frappe.get_all(
"Purchase Receipt Item",
filters={"parent": ("in", linked_purchase_receipts)},
fields=["name", "provisional_expense_account", "qty", "base_rate"],
)
default_provisional_account = self.get_company_default("default_provisional_account")
provisional_accounts = set(
[
d.provisional_expense_account
if d.provisional_expense_account
else default_provisional_account
for d in pr_items
]
)
provisional_gl_entries = frappe.get_all(
"GL Entry",
filters={
"voucher_type": "Purchase Receipt",
"voucher_no": ("in", linked_purchase_receipts),
"account": ("in", provisional_accounts),
"is_cancelled": 0,
},
fields=["voucher_detail_no"],
)
rows_with_provisional_entries = [d.voucher_detail_no for d in provisional_gl_entries]
for item in pr_items:
self.provisional_accounts[item.name] = {
"provisional_account": item.provisional_expense_account or default_provisional_account,
"qty": item.qty,
"base_rate": item.base_rate,
"has_provisional_entry": item.name in rows_with_provisional_entries,
}
def make_provisional_gl_entry(self, gl_entries, item):
if item.purchase_receipt:
pr_item = self.provisional_accounts.get(item.pr_detail, {})
if pr_item.get("has_provisional_entry"):
purchase_receipt_doc = frappe.get_cached_doc("Purchase Receipt", item.purchase_receipt)
# Intentionally passing purchase invoice item to handle partial billing
purchase_receipt_doc.add_provisional_gl_entry(
item,
gl_entries,
self.posting_date,
pr_item.get("provisional_account"),
reverse=1,
item_amount=(min(item.qty, pr_item.get("qty")) * pr_item.get("base_rate")),
)
def update_gross_purchase_amount_for_linked_assets(self, item):
assets = frappe.db.get_all(
"Asset",
@@ -1668,6 +1702,9 @@ class PurchaseInvoice(BuyingController):
self.db_set("release_date", None)
def set_tax_withholding(self):
self.set("advance_tax", [])
self.set("tax_withheld_vouchers", [])
if not self.apply_tds:
return
@@ -1709,8 +1746,6 @@ class PurchaseInvoice(BuyingController):
self.remove(d)
## Add pending vouchers on which tax was withheld
self.set("tax_withheld_vouchers", [])
for voucher_no, voucher_details in voucher_wise_amount.items():
self.append(
"tax_withheld_vouchers",
@@ -1725,7 +1760,6 @@ class PurchaseInvoice(BuyingController):
self.calculate_taxes_and_totals()
def allocate_advance_tds(self, tax_withholding_details, advance_taxes):
self.set("advance_tax", [])
for tax in advance_taxes:
allocated_amount = 0
pending_amount = flt(tax.tax_amount - tax.allocated_amount)

View File

@@ -10,7 +10,11 @@ import erpnext
from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_purchase_invoice
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_invoice as make_pi_from_po
from erpnext.buying.doctype.purchase_order.test_purchase_order import (
create_pr_against_po,
create_purchase_order,
)
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
from erpnext.controllers.accounts_controller import get_payment_terms
from erpnext.controllers.buying_controller import QtyMismatchError
@@ -2001,18 +2005,15 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
check_gl_entries(self, pi.name, expected_gle, nowdate())
pi.items[0].expense_account = "Service - _TC"
# Ledger reposted implicitly upon 'Update After Submit'
pi.save()
pi.load_from_db()
self.assertTrue(pi.repost_required)
pi.repost_accounting_entries()
expected_gle = [
["Creditors - _TC", 0.0, 1000, nowdate()],
["Service - _TC", 1000, 0.0, nowdate()],
]
check_gl_entries(self, pi.name, expected_gle, nowdate())
pi.load_from_db()
self.assertFalse(pi.repost_required)
@change_settings("Buying Settings", {"supplier_group": None})
def test_purchase_invoice_without_supplier_group(self):
@@ -2185,6 +2186,56 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
self.assertEqual(row.serial_no, "\n".join(serial_nos[:2]))
self.assertEqual(row.rejected_serial_no, serial_nos[2])
def test_make_pr_and_pi_from_po(self):
from erpnext.assets.doctype.asset.test_asset import create_asset_category
if not frappe.db.exists("Asset Category", "Computers"):
create_asset_category()
item = create_item(
item_code="_Test_Item", is_stock_item=0, is_fixed_asset=1, asset_category="Computers"
)
po = create_purchase_order(item_code=item.item_code)
pr = create_pr_against_po(po.name, 10)
pi = make_pi_from_po(po.name)
pi.insert()
pi.submit()
pr_gl_entries = frappe.db.sql(
"""select account, debit, credit
from `tabGL Entry` where voucher_type='Purchase Receipt' and voucher_no=%s
order by account asc""",
pr.name,
as_dict=1,
)
pr_expected_values = [
["Asset Received But Not Billed - _TC", 0, 5000],
["CWIP Account - _TC", 5000, 0],
]
for i, gle in enumerate(pr_gl_entries):
self.assertEqual(pr_expected_values[i][0], gle.account)
self.assertEqual(pr_expected_values[i][1], gle.debit)
self.assertEqual(pr_expected_values[i][2], gle.credit)
pi_gl_entries = frappe.db.sql(
"""select account, debit, credit
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
order by account asc""",
pi.name,
as_dict=1,
)
pi_expected_values = [
["Asset Received But Not Billed - _TC", 5000, 0],
["Creditors - _TC", 0, 5000],
]
for i, gle in enumerate(pi_gl_entries):
self.assertEqual(pi_expected_values[i][0], gle.account)
self.assertEqual(pi_expected_values[i][1], gle.debit)
self.assertEqual(pi_expected_values[i][2], gle.credit)
def set_advance_flag(company, flag, default_account):
frappe.db.set_value(

View File

@@ -68,31 +68,6 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
this.frm.toggle_reqd("due_date", !this.frm.doc.is_return);
if (this.frm.doc.repost_required && this.frm.doc.docstatus === 1) {
this.frm.set_intro(
__(
"Accounting entries for this invoice needs to be reposted. Please click on 'Repost' button to update."
)
);
this.frm
.add_custom_button(__("Repost Accounting Entries"), () => {
this.frm.call({
doc: this.frm.doc,
method: "repost_accounting_entries",
freeze: true,
freeze_message: __("Reposting..."),
callback: (r) => {
if (!r.exc) {
frappe.msgprint(__("Accounting Entries are reposted"));
me.frm.refresh();
}
},
});
})
.removeClass("btn-default")
.addClass("btn-warning");
}
if (this.frm.doc.is_return) {
this.frm.return_print_format = "Sales Invoice Return";
}
@@ -157,7 +132,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
const payment_is_overdue = doc.payment_schedule
.map((row) => Date.parse(row.due_date) < Date.now())
.reduce((prev, current) => prev || current);
.reduce((prev, current) => prev || current, false);
if (payment_is_overdue) {
this.frm.add_custom_button(
@@ -502,9 +477,13 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
frappe.msgprint(__("Please specify Company to proceed"));
} else {
var me = this;
const for_validate = me.frm.doc.is_return ? true : false;
return this.frm.call({
doc: me.frm.doc,
method: "set_missing_values",
args: {
for_validate: for_validate,
},
callback: function (r) {
if (!r.exc) {
if (r.message && r.message.print_format) {

View File

@@ -213,7 +213,6 @@
"is_internal_customer",
"is_discounted",
"remarks",
"repost_required",
"connections_tab"
],
"fields": [
@@ -2125,15 +2124,6 @@
"label": "Write Off",
"width": "50%"
},
{
"default": "0",
"fieldname": "repost_required",
"fieldtype": "Check",
"hidden": 1,
"label": "Repost Required",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "incoterm",
"fieldtype": "Link",
@@ -2188,7 +2178,7 @@
"link_fieldname": "consolidated_invoice"
}
],
"modified": "2024-06-07 16:49:32.458402",
"modified": "2024-07-18 15:30:39.428519",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -161,7 +161,6 @@ class SalesInvoice(SellingController):
project: DF.Link | None
redeem_loyalty_points: DF.Check
remarks: DF.SmallText | None
repost_required: DF.Check
represents_company: DF.Link | None
return_against: DF.Link | None
rounded_total: DF.Currency
@@ -556,7 +555,6 @@ class SalesInvoice(SellingController):
self.repost_future_sle_and_gle()
self.db_set("status", "Cancelled")
self.db_set("repost_required", 0)
if frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction":
update_company_current_month_sales(self.company)
@@ -706,24 +704,23 @@ class SalesInvoice(SellingController):
data.sales_invoice = sales_invoice
def on_update_after_submit(self):
if hasattr(self, "repost_required"):
fields_to_check = [
"additional_discount_account",
"cash_bank_account",
"account_for_change_amount",
"write_off_account",
"loyalty_redemption_account",
"unrealized_profit_loss_account",
"is_opening",
]
child_tables = {
"items": ("income_account", "expense_account", "discount_account"),
"taxes": ("account_head",),
}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.db_set("repost_required", self.needs_repost)
fields_to_check = [
"additional_discount_account",
"cash_bank_account",
"account_for_change_amount",
"write_off_account",
"loyalty_redemption_account",
"unrealized_profit_loss_account",
"is_opening",
]
child_tables = {
"items": ("income_account", "expense_account", "discount_account"),
"taxes": ("account_head",),
}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.repost_accounting_entries()
def set_paid_amount(self):
paid_amount = 0.0
@@ -1212,6 +1209,8 @@ class SalesInvoice(SellingController):
self.make_precision_loss_gl_entry(gl_entries)
self.make_discount_gl_entries(gl_entries)
gl_entries = make_regional_gl_entries(gl_entries, self)
# merge gl entries before adding pos entries
gl_entries = merge_similar_entries(gl_entries)
@@ -2225,6 +2224,11 @@ def make_inter_company_purchase_invoice(source_name, target_doc=None):
return make_inter_company_transaction("Sales Invoice", source_name, target_doc)
@erpnext.allow_regional
def make_regional_gl_entries(gl_entries, doc):
return gl_entries
def make_inter_company_transaction(doctype, source_name, target_doc=None):
if doctype in ["Sales Invoice", "Sales Order"]:
source_doc = frappe.get_doc(doctype, source_name)

View File

@@ -2202,13 +2202,14 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(si.total_taxes_and_charges, 228.82)
self.assertEqual(si.rounding_adjustment, -0.01)
expected_values = [
["_Test Account Service Tax - _TC", 0.0, 114.41],
["_Test Account VAT - _TC", 0.0, 114.41],
[si.debit_to, 1500, 0.0],
["Round Off - _TC", 0.01, 0.01],
["Sales - _TC", 0.0, 1271.18],
]
round_off_account = frappe.get_cached_value("Company", "_Test Company", "round_off_account")
expected_values = {
"_Test Account Service Tax - _TC": [0.0, 114.41],
"_Test Account VAT - _TC": [0.0, 114.41],
si.debit_to: [1500, 0.0],
round_off_account: [0.01, 0.01],
"Sales - _TC": [0.0, 1271.18],
}
gl_entries = frappe.db.sql(
"""select account, sum(debit) as debit, sum(credit) as credit
@@ -2219,10 +2220,10 @@ class TestSalesInvoice(FrappeTestCase):
as_dict=1,
)
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[i][0], gle.account)
self.assertEqual(expected_values[i][1], gle.debit)
self.assertEqual(expected_values[i][2], gle.credit)
for gle in gl_entries:
expected_account_values = expected_values[gle.account]
self.assertEqual(expected_account_values[0], gle.debit)
self.assertEqual(expected_account_values[1], gle.credit)
def test_rounding_adjustment_3(self):
from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import (
@@ -2270,6 +2271,7 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(si.total_taxes_and_charges, 480.86)
self.assertEqual(si.rounding_adjustment, -0.02)
round_off_account = frappe.get_cached_value("Company", "_Test Company", "round_off_account")
expected_values = dict(
(d[0], d)
for d in [
@@ -2277,7 +2279,7 @@ class TestSalesInvoice(FrappeTestCase):
["_Test Account Service Tax - _TC", 0.0, 240.43],
["_Test Account VAT - _TC", 0.0, 240.43],
["Sales - _TC", 0.0, 4007.15],
["Round Off - _TC", 0.02, 0.01],
[round_off_account, 0.02, 0.01],
]
)
@@ -2306,8 +2308,9 @@ class TestSalesInvoice(FrappeTestCase):
as_dict=1,
)
self.assertEqual(round_off_gle.cost_center, "_Test Cost Center 2 - _TC")
self.assertEqual(round_off_gle.location, "Block 1")
if round_off_gle:
self.assertEqual(round_off_gle.cost_center, "_Test Cost Center 2 - _TC")
self.assertEqual(round_off_gle.location, "Block 1")
disable_dimension()
@@ -2937,13 +2940,9 @@ class TestSalesInvoice(FrappeTestCase):
si.items[0].income_account = "Service - _TC"
si.additional_discount_account = "_Test Account Sales - _TC"
si.taxes[0].account_head = "TDS Payable - _TC"
# Ledger reposted implicitly upon 'Update After Submit'
si.save()
si.load_from_db()
self.assertTrue(si.repost_required)
si.repost_accounting_entries()
expected_gle = [
["_Test Account Sales - _TC", 22.0, 0.0, nowdate()],
["Debtors - _TC", 88, 0.0, nowdate()],
@@ -2953,9 +2952,6 @@ class TestSalesInvoice(FrappeTestCase):
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
si.load_from_db()
self.assertFalse(si.repost_required)
def test_asset_depreciation_on_sale_with_pro_rata(self):
"""
Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale.

View File

@@ -268,6 +268,11 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
vouchers, voucher_wise_amount = get_invoice_vouchers(
parties, tax_details, inv.company, party_type=party_type
)
payment_entry_vouchers = get_payment_entry_vouchers(
parties, tax_details, inv.company, party_type=party_type
)
advance_vouchers = get_advance_vouchers(
parties,
company=inv.company,
@@ -275,7 +280,8 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
to_date=tax_details.to_date,
party_type=party_type,
)
taxable_vouchers = vouchers + advance_vouchers
taxable_vouchers = vouchers + advance_vouchers + payment_entry_vouchers
tax_deducted_on_advances = 0
if inv.doctype == "Purchase Invoice":
@@ -387,6 +393,20 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
return vouchers, voucher_wise_amount
def get_payment_entry_vouchers(parties, tax_details, company, party_type="Supplier"):
payment_entry_filters = {
"party_type": party_type,
"party": ("in", parties),
"docstatus": 1,
"apply_tax_withholding_amount": 1,
"posting_date": ["between", (tax_details.from_date, tax_details.to_date)],
"tax_withholding_category": tax_details.get("tax_withholding_category"),
"company": company,
}
return frappe.db.get_all("Payment Entry", filters=payment_entry_filters, pluck="name")
def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type="Supplier"):
"""
Use Payment Ledger to fetch unallocated Advance Payments

View File

@@ -107,7 +107,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
self.assertEqual(len(pe.references), 1)
self.assertEqual(pe.unallocated_amount, 100)
def test_02_unreconcile_one_payment_from_multi_payments(self):
def test_02_unreconcile_one_payment_among_multi_payments(self):
"""
Scenario: 2 payments, both split against 2 different invoices
Unreconcile only one payment from one invoice

View File

@@ -7,7 +7,7 @@ import copy
import frappe
from frappe import _
from frappe.model.meta import get_field_precision
from frappe.utils import cint, cstr, flt, formatdate, getdate, now
from frappe.utils import cint, flt, formatdate, getdate, now
import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@@ -228,11 +228,13 @@ def get_cost_center_allocation_data(company, posting_date):
def merge_similar_entries(gl_map, precision=None):
merged_gl_map = []
accounting_dimensions = get_accounting_dimensions()
merge_properties = get_merge_properties(accounting_dimensions)
for entry in gl_map:
entry.merge_key = get_merge_key(entry, merge_properties)
# if there is already an entry in this account then just add it
# to that entry
same_head = check_if_in_list(entry, merged_gl_map, accounting_dimensions)
same_head = check_if_in_list(entry, merged_gl_map)
if same_head:
same_head.debit = flt(same_head.debit) + flt(entry.debit)
same_head.debit_in_account_currency = flt(same_head.debit_in_account_currency) + flt(
@@ -273,40 +275,53 @@ def merge_similar_entries(gl_map, precision=None):
return merged_gl_map
def check_if_in_list(gle, gl_map, dimensions=None):
account_head_fieldnames = [
"voucher_detail_no",
"party",
"against_voucher",
def get_merge_properties(dimensions=None):
merge_properties = [
"account",
"cost_center",
"against_voucher_type",
"party",
"party_type",
"voucher_detail_no",
"against_voucher",
"against_voucher_type",
"project",
"finance_book",
"voucher_no",
]
if dimensions:
account_head_fieldnames = account_head_fieldnames + dimensions
merge_properties.extend(dimensions)
return merge_properties
def get_merge_key(entry, merge_properties):
merge_key = []
for fieldname in merge_properties:
merge_key.append(entry.get(fieldname, ""))
return tuple(merge_key)
def check_if_in_list(gle, gl_map):
for e in gl_map:
same_head = True
if e.account != gle.account:
same_head = False
continue
for fieldname in account_head_fieldnames:
if cstr(e.get(fieldname)) != cstr(gle.get(fieldname)):
same_head = False
break
if same_head:
if e.merge_key == gle.merge_key:
return e
def toggle_debit_credit_if_negative(gl_map):
for entry in gl_map:
# toggle debit, credit if negative entry
if flt(entry.debit) < 0 and flt(entry.credit) < 0 and flt(entry.debit) == flt(entry.credit):
entry.credit *= -1
entry.debit *= -1
if (
flt(entry.debit_in_account_currency) < 0
and flt(entry.credit_in_account_currency) < 0
and flt(entry.debit_in_account_currency) == flt(entry.credit_in_account_currency)
):
entry.credit_in_account_currency *= -1
entry.debit_in_account_currency *= -1
if flt(entry.debit) < 0:
entry.credit = flt(entry.credit) - flt(entry.debit)
entry.debit = 0.0

View File

@@ -7,7 +7,7 @@ from erpnext.accounts.report.accounts_payable.accounts_payable import execute
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
class TestAccountsPayable(AccountsTestMixin, FrappeTestCase):
def setUp(self):
self.create_company()
self.create_customer()

View File

@@ -139,6 +139,7 @@ class ReceivablePayableReport:
paid_in_account_currency=0.0,
credit_note_in_account_currency=0.0,
outstanding_in_account_currency=0.0,
cost_center=ple.cost_center,
)
self.get_invoices(ple)
@@ -253,7 +254,7 @@ class ReceivablePayableReport:
row.paid -= amount
row.paid_in_account_currency -= amount_in_account_currency
if ple.cost_center:
if not row.cost_center and ple.cost_center:
row.cost_center = str(ple.cost_center)
def update_sub_total_row(self, row, party):
@@ -288,13 +289,13 @@ class ReceivablePayableReport:
must_consider = False
if self.filters.get("for_revaluation_journals"):
if (abs(row.outstanding) > 0.0 / 10**self.currency_precision) or (
abs(row.outstanding_in_account_currency) > 0.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:
if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) and (
(abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision)
if (abs(row.outstanding) >= 1.0 / 10**self.currency_precision) and (
(abs(row.outstanding_in_account_currency) >= 1.0 / 10**self.currency_precision)
or (row.voucher_no in self.err_journals)
):
must_consider = True

View File

@@ -53,11 +53,13 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
si = si.submit()
return si
def create_payment_entry(self, docname):
def create_payment_entry(self, docname, do_not_submit=False):
pe = get_payment_entry("Sales Invoice", docname, bank_account=self.cash, party_amount=40)
pe.paid_from = self.debit_to
pe.insert()
pe.submit()
if not do_not_submit:
pe.submit()
return pe
def create_credit_note(self, docname, do_not_submit=False):
credit_note = create_sales_invoice(
@@ -955,3 +957,69 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
self.assertEqual(
expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount]
)
def test_accounts_receivable_output_for_minor_outstanding(self):
"""
AR/AP should report miniscule outstanding of 0.01. Or else there will be slight difference with General Ledger/Trial Balance
"""
filters = {
"company": self.company,
"report_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
}
# check invoice grand total and invoiced column's value for 3 payment terms
si = self.create_sales_invoice(no_payment_schedule=True)
pe = get_payment_entry("Sales Invoice", si.name, bank_account=self.cash, party_amount=99.99)
pe.paid_from = self.debit_to
pe.save().submit()
report = execute(filters)
expected_data_after_payment = [100, 100, 99.99, 0.01]
self.assertEqual(len(report[1]), 1)
row = report[1][0]
self.assertEqual(
expected_data_after_payment,
[row.invoice_grand_total, row.invoiced, row.paid, row.outstanding],
)
def test_cost_center_on_report_output(self):
filters = {
"company": self.company,
"report_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
}
# check invoice grand total and invoiced column's value for 3 payment terms
si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
si.cost_center = self.cost_center
si.save().submit()
new_cc = frappe.get_doc(
{
"doctype": "Cost Center",
"cost_center_name": "East Wing",
"parent_cost_center": self.company + " - " + self.company_abbr,
"company": self.company,
}
)
new_cc.save()
# check invoice grand total, invoiced, paid and outstanding column's value after payment
pe = self.create_payment_entry(si.name, do_not_submit=True)
pe.cost_center = new_cc.name
pe.save().submit()
report = execute(filters)
expected_data_after_payment = [si.name, si.cost_center, 60]
self.assertEqual(len(report[1]), 1)
row = report[1][0]
self.assertEqual(expected_data_after_payment, [row.voucher_no, row.cost_center, row.outstanding])

View File

@@ -109,7 +109,7 @@ def get_provisional_profit_loss(
):
provisional_profit_loss = {}
total_row = {}
if asset and (liability or equity):
if asset:
total = total_row_total = 0
currency = currency or frappe.get_cached_value("Company", company, "default_currency")
total_row = {
@@ -122,14 +122,16 @@ def get_provisional_profit_loss(
for period in period_list:
key = period if consolidated else period.key
effective_liability = 0.0
if liability:
effective_liability += flt(liability[-2].get(key))
if equity:
effective_liability += flt(equity[-2].get(key))
total_assets = flt(asset[0].get(key))
effective_liability = 0.00
provisional_profit_loss[key] = flt(asset[-2].get(key)) - effective_liability
total_row[key] = effective_liability + provisional_profit_loss[key]
if liability:
effective_liability += flt(liability[0].get(key))
if equity:
effective_liability += flt(equity[0].get(key))
provisional_profit_loss[key] = total_assets - effective_liability
total_row[key] = provisional_profit_loss[key] + effective_liability
if provisional_profit_loss[key]:
has_value = True

View File

@@ -2,10 +2,12 @@
# License: GNU General Public License v3. See license.txt
import copy
from collections import OrderedDict
import frappe
from frappe import _, _dict
from frappe.query_builder import Criterion
from frappe.utils import cstr, getdate
from erpnext import get_company_currency, get_default_company
@@ -17,9 +19,6 @@ from erpnext.accounts.report.financial_statements import get_cost_centers_with_c
from erpnext.accounts.report.utils import convert_to_presentation_currency, get_currency
from erpnext.accounts.utils import get_account_currency
# to cache translations
TRANSLATIONS = frappe._dict()
def execute(filters=None):
if not filters:
@@ -44,19 +43,11 @@ def execute(filters=None):
columns = get_columns(filters)
update_translations()
res = get_result(filters, account_details)
return columns, res
def update_translations():
TRANSLATIONS.update(
dict(OPENING=_("Opening"), TOTAL=_("Total"), CLOSING_TOTAL=_("Closing (Opening + Total)"))
)
def validate_filters(filters, account_details):
if not filters.get("company"):
frappe.throw(_("{0} is mandatory").format(_("Company")))
@@ -319,26 +310,31 @@ def get_accounts_with_children(accounts):
if not isinstance(accounts, list):
accounts = [d.strip() for d in accounts.strip().split(",") if d]
all_accounts = []
for d in accounts:
account = frappe.get_cached_doc("Account", d)
if account:
children = frappe.get_all(
"Account", filters={"lft": [">=", account.lft], "rgt": ["<=", account.rgt]}
)
all_accounts += [c.name for c in children]
else:
frappe.throw(_("Account: {0} does not exist").format(d))
if not accounts:
return
return list(set(all_accounts)) if all_accounts else None
doctype = frappe.qb.DocType("Account")
accounts_data = (
frappe.qb.from_(doctype)
.select(doctype.lft, doctype.rgt)
.where(doctype.name.isin(accounts))
.run(as_dict=True)
)
conditions = []
for account in accounts_data:
conditions.append((doctype.lft >= account.lft) & (doctype.rgt <= account.rgt))
return frappe.qb.from_(doctype).select(doctype.name).where(Criterion.any(conditions)).run(pluck=True)
def get_data_with_opening_closing(filters, account_details, accounting_dimensions, gl_entries):
data = []
totals_dict = get_totals_dict()
gle_map = initialize_gle_map(gl_entries, filters)
gle_map = initialize_gle_map(gl_entries, filters, totals_dict)
totals, entries = get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map)
totals, entries = get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map, totals_dict)
# Opening for filtered account
data.append(totals.opening)
@@ -387,9 +383,9 @@ def get_totals_dict():
)
return _dict(
opening=_get_debit_credit_dict(TRANSLATIONS.OPENING),
total=_get_debit_credit_dict(TRANSLATIONS.TOTAL),
closing=_get_debit_credit_dict(TRANSLATIONS.CLOSING_TOTAL),
opening=_get_debit_credit_dict(_("Opening")),
total=_get_debit_credit_dict(_("Total")),
closing=_get_debit_credit_dict(_("Closing (Opening + Total)")),
)
@@ -402,17 +398,16 @@ def group_by_field(group_by):
return "voucher_no"
def initialize_gle_map(gl_entries, filters):
def initialize_gle_map(gl_entries, filters, totals_dict):
gle_map = OrderedDict()
group_by = group_by_field(filters.get("group_by"))
for gle in gl_entries:
gle_map.setdefault(gle.get(group_by), _dict(totals=get_totals_dict(), entries=[]))
gle_map.setdefault(gle.get(group_by), _dict(totals=copy.deepcopy(totals_dict), entries=[]))
return gle_map
def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map):
totals = get_totals_dict()
def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map, totals):
entries = []
consolidated_gle = OrderedDict()
group_by = group_by_field(filters.get("group_by"))

View File

@@ -713,7 +713,8 @@ class GrossProfitGenerator:
def get_average_buying_rate(self, row, item_code):
args = row
if item_code not in self.average_buying_rate:
key = (item_code, row.warehouse)
if key not in self.average_buying_rate:
args.update(
{
"voucher_type": row.parenttype,
@@ -727,9 +728,9 @@ class GrossProfitGenerator:
args.update({"serial_and_batch_bundle": row.serial_and_batch_bundle})
average_buying_rate = get_incoming_rate(args)
self.average_buying_rate[item_code] = flt(average_buying_rate)
self.average_buying_rate[key] = flt(average_buying_rate)
return self.average_buying_rate[item_code]
return self.average_buying_rate[key]
def get_last_purchase_rate(self, item_code, row):
purchase_invoice = frappe.qb.DocType("Purchase Invoice")

View File

@@ -558,3 +558,50 @@ class TestGrossProfit(FrappeTestCase):
}
gp_entry = [x for x in data if x.parent_invoice == sinv.name]
self.assertDictContainsSubset(expected_entry, gp_entry[0])
def test_valuation_rate_without_previous_sle(self):
"""
Test Valuation rate calculation when stock ledger is empty and invoices are against different warehouses
"""
stock_settings = frappe.get_doc("Stock Settings")
stock_settings.valuation_method = "FIFO"
stock_settings.save()
item = create_item(
item_code="_Test Wirebound Notebook",
is_stock_item=1,
)
item.allow_negative_stock = True
item.save()
self.item = item.item_code
item.reload()
item.valuation_rate = 1900
item.save()
sinv1 = self.create_sales_invoice(qty=1, rate=2000, posting_date=nowdate(), do_not_submit=True)
sinv1.update_stock = 1
sinv1.set_warehouse = self.warehouse
sinv1.items[0].warehouse = self.warehouse
sinv1.save().submit()
item.reload()
item.valuation_rate = 1800
item.save()
sinv2 = self.create_sales_invoice(qty=1, rate=2000, posting_date=nowdate(), do_not_submit=True)
sinv2.update_stock = 1
sinv2.set_warehouse = self.finished_warehouse
sinv2.items[0].warehouse = self.finished_warehouse
sinv2.save().submit()
filters = frappe._dict(
company=self.company, from_date=nowdate(), to_date=nowdate(), group_by="Invoice"
)
columns, data = execute(filters=filters)
item_from_sinv1 = [x for x in data if x.parent_invoice == sinv1.name]
self.assertEqual(len(item_from_sinv1), 1)
self.assertEqual(1900, item_from_sinv1[0].valuation_rate)
item_from_sinv2 = [x for x in data if x.parent_invoice == sinv2.name]
self.assertEqual(len(item_from_sinv2), 1)
self.assertEqual(1800, item_from_sinv2[0].valuation_rate)

View File

@@ -52,7 +52,7 @@ frappe.query_reports["Item-wise Purchase Register"] = {
label: __("Group By"),
fieldname: "group_by",
fieldtype: "Select",
options: ["Supplier", "Item Group", "Item", "Invoice"],
options: ["", "Supplier", "Item Group", "Item", "Invoice"],
},
],
formatter: function (value, row, column, data, default_formatter) {

View File

@@ -309,14 +309,15 @@ def apply_conditions(query, pi, pii, filters):
query = query.orderby(pi.posting_date, order=Order.desc)
query = query.orderby(pii.item_group, order=Order.desc)
else:
query = apply_group_by_conditions(filters, "Purchase Invoice")
query = apply_group_by_conditions(query, pi, pii, filters)
return query
def get_items(filters, additional_table_columns):
pi = frappe.qb.DocType("Purchase Invoice")
pii = frappe.qb.DocType("Purchase Invoice Item")
doctype = "Purchase Invoice"
pi = frappe.qb.DocType(doctype)
pii = frappe.qb.DocType(f"{doctype} Item")
Item = frappe.qb.DocType("Item")
query = (
frappe.qb.from_(pi)
@@ -353,6 +354,7 @@ def get_items(filters, additional_table_columns):
pi.mode_of_payment,
)
.where(pi.docstatus == 1)
.where(pii.parenttype == doctype)
)
if filters.get("supplier"):

View File

@@ -0,0 +1,63 @@
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import getdate, today
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.report.item_wise_purchase_register.item_wise_purchase_register import execute
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
class TestItemWisePurchaseRegister(AccountsTestMixin, FrappeTestCase):
def setUp(self):
self.create_company()
self.create_supplier()
self.create_item()
def tearDown(self):
frappe.db.rollback()
def create_purchase_invoice(self, do_not_submit=False):
pi = make_purchase_invoice(
item=self.item,
company=self.company,
supplier=self.supplier,
is_return=False,
update_stock=False,
do_not_save=1,
rate=100,
price_list_rate=100,
qty=1,
)
pi = pi.save()
if not do_not_submit:
pi = pi.submit()
return pi
def test_basic_report_output(self):
pi = self.create_purchase_invoice()
filters = frappe._dict({"from_date": today(), "to_date": today(), "company": self.company})
report = execute(filters)
self.assertEqual(len(report[1]), 1)
expected_result = {
"item_code": pi.items[0].item_code,
"invoice": pi.name,
"posting_date": getdate(),
"supplier": pi.supplier,
"credit_to": pi.credit_to,
"company": self.company,
"expense_account": pi.items[0].expense_account,
"stock_qty": 1.0,
"stock_uom": pi.items[0].stock_uom,
"rate": 100.0,
"amount": 100.0,
"total_tax": 0,
"total": 100.0,
"currency": "INR",
}
report_output = {k: v for k, v in report[1][0].items() if k in expected_result}
self.assertDictEqual(report_output, expected_result)

View File

@@ -70,7 +70,7 @@ frappe.query_reports["Item-wise Sales Register"] = {
label: __("Group By"),
fieldname: "group_by",
fieldtype: "Select",
options: ["Customer Group", "Customer", "Item Group", "Item", "Territory", "Invoice"],
options: ["", "Customer Group", "Customer", "Item Group", "Item", "Territory", "Invoice"],
},
],
formatter: function (value, row, column, data, default_formatter) {

View File

@@ -410,8 +410,9 @@ def apply_group_by_conditions(query, si, ii, filters):
def get_items(filters, additional_query_columns, additional_conditions=None):
si = frappe.qb.DocType("Sales Invoice")
sii = frappe.qb.DocType("Sales Invoice Item")
doctype = "Sales Invoice"
si = frappe.qb.DocType(doctype)
sii = frappe.qb.DocType(f"{doctype} Item")
item = frappe.qb.DocType("Item")
query = (
@@ -459,6 +460,7 @@ def get_items(filters, additional_query_columns, additional_conditions=None):
sii.qty,
)
.where(si.docstatus == 1)
.where(sii.parenttype == doctype)
)
if additional_query_columns:

View File

@@ -0,0 +1,65 @@
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import getdate, today
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import execute
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
class TestItemWiseSalesRegister(AccountsTestMixin, FrappeTestCase):
def setUp(self):
self.create_company()
self.create_customer()
self.create_item()
self.clear_old_entries()
def tearDown(self):
frappe.db.rollback()
def create_sales_invoice(self, do_not_submit=False):
si = create_sales_invoice(
item=self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
posting_date=today(),
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
rate=100,
price_list_rate=100,
do_not_save=1,
)
si = si.save()
if not do_not_submit:
si = si.submit()
return si
def test_basic_report_output(self):
si = self.create_sales_invoice()
filters = frappe._dict({"from_date": today(), "to_date": today(), "company": self.company})
report = execute(filters)
self.assertEqual(len(report[1]), 1)
expected_result = {
"item_code": si.items[0].item_code,
"invoice": si.name,
"posting_date": getdate(),
"customer": si.customer,
"debit_to": si.debit_to,
"company": self.company,
"income_account": si.items[0].income_account,
"stock_qty": 1.0,
"stock_uom": si.items[0].stock_uom,
"rate": 100.0,
"amount": 100.0,
"total_tax": 0,
"total_other_charges": 0,
"total": 100.0,
"currency": "INR",
}
report_output = {k: v for k, v in report[1][0].items() if k in expected_result}
self.assertDictEqual(report_output, expected_result)

View File

@@ -12,7 +12,7 @@ def execute(filters=None):
else:
party_naming_by = frappe.db.get_single_value("Buying Settings", "supp_master_name")
filters.update({"naming_series": party_naming_by})
filters["naming_series"] = party_naming_by
validate_filters(filters)
(
@@ -63,21 +63,23 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
tax_withholding_category = tds_accounts.get(entry.account)
# or else the consolidated value from the voucher document
if not tax_withholding_category:
tax_withholding_category = tax_category_map.get(name)
tax_withholding_category = tax_category_map.get((voucher_type, name))
# or else from the party default
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 net_total_map.get((voucher_type, name)):
if voucher_type == "Journal Entry":
# back calcalute total amount from rate and tax_amount
if rate:
total_amount = grand_total = base_total = tax_amount / (rate / 100)
elif voucher_type == "Purchase Invoice":
total_amount, grand_total, base_total, bill_no, bill_date = net_total_map.get(name)
total_amount, grand_total, base_total, bill_no, bill_date = net_total_map.get(
(voucher_type, name)
)
else:
total_amount, grand_total, base_total = net_total_map.get(name)
total_amount, grand_total, base_total = net_total_map.get((voucher_type, name))
else:
total_amount += entry.credit
@@ -97,7 +99,7 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
}
if filters.naming_series == "Naming Series":
row.update({"party_name": party_map.get(party, {}).get(party_name)})
row["party_name"] = party_map.get(party, {}).get(party_name)
row.update(
{
@@ -279,7 +281,6 @@ def get_tds_docs(filters):
journal_entries = []
tax_category_map = frappe._dict()
net_total_map = frappe._dict()
frappe._dict()
journal_entry_party_map = frappe._dict()
bank_accounts = frappe.get_all("Account", {"is_group": 0, "account_type": "Bank"}, pluck="name")
@@ -412,7 +413,7 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None):
)
for entry in entries:
tax_category_map.update({entry.name: entry.tax_withholding_category})
tax_category_map[(doctype, entry.name)] = entry.tax_withholding_category
if doctype == "Purchase Invoice":
value = [
entry.base_tax_withholding_net_total,
@@ -427,7 +428,8 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None):
value = [entry.paid_amount, entry.paid_amount_after_tax, entry.base_paid_amount]
else:
value = [entry.total_amount] * 3
net_total_map.update({entry.name: value})
net_total_map[(doctype, entry.name)] = value
def get_tax_rate_map(filters):

View File

@@ -10,7 +10,7 @@ import frappe.defaults
from frappe import _, qb, throw
from frappe.model.meta import get_field_precision
from frappe.query_builder import AliasedQuery, Criterion, Table
from frappe.query_builder.functions import Sum
from frappe.query_builder.functions import Count, Sum
from frappe.query_builder.utils import DocType
from frappe.utils import (
add_days,
@@ -1492,24 +1492,39 @@ def get_stock_accounts(company, voucher_type=None, voucher_no=None):
)
]
return stock_accounts
return list(set(stock_accounts))
def get_stock_and_account_balance(account=None, posting_date=None, company=None):
if not posting_date:
posting_date = nowdate()
warehouse_account = get_warehouse_account_map(company)
account_balance = get_balance_on(
account, posting_date, in_account_currency=False, ignore_account_permission=True
)
related_warehouses = [
wh
for wh, wh_details in warehouse_account.items()
if wh_details.account == account and not wh_details.is_group
]
account_table = frappe.qb.DocType("Account")
query = (
frappe.qb.from_(account_table)
.select(Count(account_table.name))
.where(
(account_table.account_type == "Stock")
& (account_table.company == company)
& (account_table.is_group == 0)
)
)
no_of_stock_accounts = cint(query.run()[0][0])
related_warehouses = []
if no_of_stock_accounts > 1:
warehouse_account = get_warehouse_account_map(company)
related_warehouses = [
wh
for wh, wh_details in warehouse_account.items()
if wh_details.account == account and not wh_details.is_group
]
total_stock_value = get_stock_value_on(related_warehouses, posting_date)
@@ -1583,6 +1598,18 @@ def auto_create_exchange_rate_revaluation_weekly() -> None:
create_err_and_its_journals(companies)
def auto_create_exchange_rate_revaluation_monthly() -> None:
"""
Executed by background job
"""
companies = frappe.db.get_all(
"Company",
filters={"auto_exchange_rate_revaluation": 1, "auto_err_frequency": "Montly"},
fields=["name", "submit_err_jv"],
)
create_err_and_its_journals(companies)
def get_payment_ledger_entries(gl_entries, cancel=0):
ple_map = []
if gl_entries:

View File

@@ -187,7 +187,7 @@ frappe.ui.form.on("Asset", {
if (frm.doc.docstatus == 0) {
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
if (frm.doc.is_composite_asset && !frm.doc.capitalized_in) {
if (frm.doc.is_composite_asset) {
$(".primary-action").prop("hidden", true);
$(".form-message").text("Capitalize this asset to confirm");
@@ -511,6 +511,8 @@ frappe.ui.form.on("Asset", {
frappe.call({
args: {
asset: frm.doc.name,
asset_name: frm.doc.asset_name,
item_code: frm.doc.item_code,
},
method: "erpnext.assets.doctype.asset.asset.create_asset_capitalization",
callback: function (r) {
@@ -773,11 +775,8 @@ frappe.ui.form.on("Asset Finance Book", {
depreciation_start_date: function (frm, cdt, cdn) {
const book = locals[cdt][cdn];
if (
frm.doc.available_for_use_date &&
book.depreciation_start_date == frm.doc.available_for_use_date
) {
frappe.msgprint(__("Depreciation Posting Date should not be equal to Available for Use Date."));
if (frm.doc.available_for_use_date && book.depreciation_start_date < frm.doc.available_for_use_date) {
frappe.msgprint(__("Depreciation Posting Date cannot be before Available-for-use Date"));
book.depreciation_start_date = "";
frm.refresh_field("finance_books");
}

View File

@@ -75,8 +75,7 @@
"purchase_amount",
"default_finance_book",
"depr_entry_posting_status",
"amended_from",
"capitalized_in"
"amended_from"
],
"fields": [
{
@@ -222,7 +221,7 @@
"read_only": 1
},
{
"depends_on": "eval:!(doc.is_composite_asset && !doc.capitalized_in)",
"depends_on": "eval:!doc.is_composite_asset",
"fieldname": "gross_purchase_amount",
"fieldtype": "Currency",
"label": "Gross Purchase Amount",
@@ -508,14 +507,6 @@
"fieldtype": "Check",
"label": "Is Composite Asset"
},
{
"fieldname": "capitalized_in",
"fieldtype": "Link",
"hidden": 1,
"label": "Capitalized In",
"options": "Asset Capitalization",
"read_only": 1
},
{
"depends_on": "eval:doc.docstatus > 0",
"fieldname": "total_asset_cost",
@@ -589,7 +580,7 @@
"link_fieldname": "target_asset"
}
],
"modified": "2024-05-21 13:46:21.066483",
"modified": "2024-07-07 22:27:14.733839",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",

View File

@@ -60,7 +60,6 @@ class Asset(AccountsController):
available_for_use_date: DF.Date | None
booked_fixed_asset: DF.Check
calculate_depreciation: DF.Check
capitalized_in: DF.Link | None
company: DF.Link
comprehensive_insurance: DF.Data | None
cost_center: DF.Link | None
@@ -162,7 +161,7 @@ class Asset(AccountsController):
def on_cancel(self):
self.validate_cancellation()
self.cancel_movement_entries()
self.cancel_capitalization()
self.reload()
self.delete_depreciation_entries()
cancel_asset_depr_schedules(self)
self.set_status()
@@ -268,10 +267,10 @@ class Asset(AccountsController):
frappe.throw(_("Available for use date is required"))
for d in self.finance_books:
if d.depreciation_start_date == self.available_for_use_date:
if getdate(d.depreciation_start_date) < getdate(self.available_for_use_date):
frappe.throw(
_(
"Row #{}: Depreciation Posting Date should not be equal to Available for Use Date."
"Depreciation Row {0}: Depreciation Posting Date cannot be before Available-for-use Date"
).format(d.idx),
title=_("Incorrect Date"),
)
@@ -524,16 +523,6 @@ 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 row in self.get("finance_books"):
@@ -872,10 +861,15 @@ def create_asset_repair(asset, asset_name):
@frappe.whitelist()
def create_asset_capitalization(asset):
def create_asset_capitalization(asset, asset_name, item_code):
asset_capitalization = frappe.new_doc("Asset Capitalization")
asset_capitalization.update(
{"target_asset": asset, "capitalization_method": "Choose a WIP composite asset"}
{
"target_asset": asset,
"capitalization_method": "Choose a WIP composite asset",
"target_asset_name": asset_name,
"target_item_code": item_code,
}
)
return asset_capitalization

View File

@@ -740,7 +740,7 @@ class TestDepreciationMethods(AssetSetup):
available_for_use_date="2030-06-06",
is_existing_asset=1,
opening_number_of_booked_depreciations=2,
opening_accumulated_depreciation=47095.89,
opening_accumulated_depreciation=47178.08,
expected_value_after_useful_life=10000,
depreciation_start_date="2032-12-31",
total_number_of_depreciations=3,
@@ -748,7 +748,7 @@ class TestDepreciationMethods(AssetSetup):
)
self.assertEqual(asset.status, "Draft")
expected_schedules = [["2032-12-31", 42904.11, 90000.0]]
expected_schedules = [["2032-12-31", 30000.0, 77178.08], ["2033-06-06", 12821.92, 90000.0]]
schedules = [
[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
for d in get_depr_schedule(asset.name, "Draft")

View File

@@ -138,22 +138,10 @@ class AssetCapitalization(StockController):
"Asset",
"Asset Movement",
)
self.cancel_target_asset()
self.update_stock_ledger()
self.make_gl_entries()
self.restore_consumed_asset_items()
def on_trash(self):
frappe.db.set_value("Asset", self.target_asset, "capitalized_in", None)
super().on_trash()
def cancel_target_asset(self):
if self.entry_type == "Capitalization" and self.target_asset:
asset_doc = frappe.get_doc("Asset", self.target_asset)
asset_doc.db_set("capitalized_in", None)
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
@@ -329,8 +317,12 @@ class AssetCapitalization(StockController):
if not self.target_is_fixed_asset and not self.get("asset_items"):
frappe.throw(_("Consumed Asset Items is mandatory for Decapitalization"))
if not self.get("stock_items") and not self.get("asset_items"):
frappe.throw(_("Consumed Stock Items or Consumed Asset Items is mandatory for Capitalization"))
if not (self.get("stock_items") or self.get("asset_items") or self.get("service_items")):
frappe.throw(
_(
"Consumed Stock Items, Consumed Asset Items or Consumed Service Items is mandatory for Capitalization"
)
)
def validate_item(self, item):
from erpnext.stock.doctype.item.item import validate_end_of_life
@@ -617,7 +609,6 @@ class AssetCapitalization(StockController):
asset_doc.purchase_date = self.posting_date
asset_doc.gross_purchase_amount = total_target_asset_value
asset_doc.purchase_amount = total_target_asset_value
asset_doc.capitalized_in = self.name
asset_doc.flags.ignore_validate = True
asset_doc.flags.asset_created_via_asset_capitalization = True
asset_doc.insert()
@@ -653,7 +644,6 @@ class AssetCapitalization(StockController):
asset_doc = frappe.get_doc("Asset", self.target_asset)
asset_doc.gross_purchase_amount = total_target_asset_value
asset_doc.purchase_amount = total_target_asset_value
asset_doc.capitalized_in = self.name
asset_doc.flags.ignore_validate = True
asset_doc.save()

View File

@@ -386,6 +386,56 @@ class TestAssetCapitalization(unittest.TestCase):
self.assertFalse(get_actual_gle_dict(asset_capitalization.name))
self.assertFalse(get_actual_sle_dict(asset_capitalization.name))
def test_capitalize_only_service_item(self):
company = "_Test Company"
# Variables
service_rate = 500
service_qty = 2
service_amount = 1000
total_amount = 1000
wip_composite_asset = create_asset(
asset_name="Asset Capitalization WIP Composite Asset",
is_composite_asset=1,
warehouse="Stores - TCP1",
company=company,
)
# Create and submit Asset Captitalization
asset_capitalization = create_asset_capitalization(
entry_type="Capitalization",
capitalization_method="Choose a WIP composite asset",
target_asset=wip_composite_asset.name,
target_asset_location="Test Location",
service_qty=service_qty,
service_rate=service_rate,
service_expense_account="Expenses Included In Asset Valuation - _TC",
company=company,
submit=1,
)
self.assertEqual(asset_capitalization.service_items[0].amount, service_amount)
self.assertEqual(asset_capitalization.service_items_total, service_amount)
target_asset = frappe.get_doc("Asset", asset_capitalization.target_asset)
self.assertEqual(target_asset.gross_purchase_amount, total_amount)
self.assertEqual(target_asset.purchase_amount, total_amount)
expected_gle = {
"_Test Fixed Asset - _TC": 1000.0,
"Expenses Included In Asset Valuation - _TC": -1000.0,
}
actual_gle = get_actual_gle_dict(asset_capitalization.name)
self.assertEqual(actual_gle, expected_gle)
# Cancel Asset Capitalization and make test entries and status are reversed
asset_capitalization.cancel()
self.assertFalse(get_actual_gle_dict(asset_capitalization.name))
self.assertFalse(get_actual_sle_dict(asset_capitalization.name))
def create_asset_capitalization_data():
create_item("Capitalization Target Stock Item", is_stock_item=1, is_fixed_asset=0, is_purchase_item=0)

View File

@@ -552,9 +552,18 @@ def _check_is_pro_rata(asset_doc, row, wdv_or_dd_non_yearly=False):
# if not existing asset, from_date = available_for_use_date
# otherwise, if opening_number_of_booked_depreciations = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12
# from_date = 01/01/2022
from_date = _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly=False)
days = date_diff(row.depreciation_start_date, from_date) + 1
total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation)
if row.depreciation_method in ("Straight Line", "Manual"):
prev_depreciation_start_date = add_months(
row.depreciation_start_date,
(row.frequency_of_depreciation * -1) * asset_doc.opening_number_of_booked_depreciations,
)
from_date = asset_doc.available_for_use_date
days = date_diff(prev_depreciation_start_date, from_date) + 1
total_days = get_total_days(prev_depreciation_start_date, row.frequency_of_depreciation)
else:
from_date = _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly=False)
days = date_diff(row.depreciation_start_date, from_date) + 1
total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation)
if days <= 0:
frappe.throw(
_(
@@ -682,39 +691,75 @@ def get_straight_line_or_manual_depr_amount(
# if the Depreciation Schedule is being prepared for the first time
else:
if row.daily_prorata_based:
amount = (
flt(asset.gross_purchase_amount)
- flt(asset.opening_accumulated_depreciation)
- flt(row.expected_value_after_useful_life)
)
amount = flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
return get_daily_prorata_based_straight_line_depr(
asset, row, schedule_idx, number_of_pending_depreciations, amount
)
else:
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.opening_number_of_booked_depreciations)
depreciation_amount = (
flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
) / flt(row.total_number_of_depreciations)
return depreciation_amount
def get_daily_prorata_based_straight_line_depr(
asset, row, schedule_idx, number_of_pending_depreciations, amount
):
total_years = flt(number_of_pending_depreciations * row.frequency_of_depreciation) / 12
every_year_depr = amount / total_years
daily_depr_amount = get_daily_depr_amount(asset, row, schedule_idx, amount)
year_start_date = add_years(
row.depreciation_start_date, (row.frequency_of_depreciation * schedule_idx) // 12
)
year_end_date = add_days(add_years(year_start_date, 1), -1)
daily_depr_amount = every_year_depr / (date_diff(year_end_date, year_start_date) + 1)
from_date, total_depreciable_days = _get_total_days(
row.depreciation_start_date, schedule_idx, row.frequency_of_depreciation
)
return daily_depr_amount * total_depreciable_days
def get_daily_depr_amount(asset, row, schedule_idx, amount):
if cint(frappe.db.get_single_value("Accounts Settings", "calculate_depr_using_total_days")):
total_days = (
date_diff(
get_last_day(
add_months(
row.depreciation_start_date,
flt(
row.total_number_of_depreciations
- asset.opening_number_of_booked_depreciations
- 1
)
* row.frequency_of_depreciation,
)
),
add_days(
add_months(
row.depreciation_start_date,
(row.frequency_of_depreciation * (asset.opening_number_of_booked_depreciations + 1))
* -1,
),
1,
),
)
+ 1
)
return amount / total_days
else:
total_years = (
flt(
(row.total_number_of_depreciations - row.total_number_of_booked_depreciations)
* row.frequency_of_depreciation
)
/ 12
)
every_year_depr = amount / total_years
year_start_date = add_years(
row.depreciation_start_date, (row.frequency_of_depreciation * schedule_idx) // 12
)
year_end_date = add_days(add_years(year_start_date, 1), -1)
return every_year_depr / (date_diff(year_end_date, year_start_date) + 1)
def get_shift_depr_amount(asset_depr_schedule, asset, row, schedule_idx):
if asset_depr_schedule.get("__islocal") and not asset.flags.shift_allocation:
return (
@@ -867,7 +912,7 @@ def _get_daily_prorata_based_default_wdv_or_dd_depr_amount(
def get_monthly_depr_amount(fb_row, schedule_idx, depreciable_value):
""" "
"""
Returns monthly depreciation amount when year changes
1. Calculate per day depr based on new year
2. Calculate monthly amount based on new per day amount

View File

@@ -75,6 +75,178 @@ class TestAssetDepreciationSchedule(FrappeTestCase):
]
self.assertEqual(schedules, expected_schedules)
def test_schedule_for_slm_for_existing_asset_daily_pro_rata_enabled(self):
frappe.db.set_single_value("Accounts Settings", "calculate_depr_using_total_days", 1)
asset = create_asset(
calculate_depreciation=1,
depreciation_method="Straight Line",
available_for_use_date="2023-10-10",
is_existing_asset=1,
opening_number_of_booked_depreciations=9,
opening_accumulated_depreciation=265,
depreciation_start_date="2024-07-31",
total_number_of_depreciations=24,
frequency_of_depreciation=1,
gross_purchase_amount=731,
daily_prorata_based=1,
)
expected_schedules = [
["2024-07-31", 31.0, 296.0],
["2024-08-31", 31.0, 327.0],
["2024-09-30", 30.0, 357.0],
["2024-10-31", 31.0, 388.0],
["2024-11-30", 30.0, 418.0],
["2024-12-31", 31.0, 449.0],
["2025-01-31", 31.0, 480.0],
["2025-02-28", 28.0, 508.0],
["2025-03-31", 31.0, 539.0],
["2025-04-30", 30.0, 569.0],
["2025-05-31", 31.0, 600.0],
["2025-06-30", 30.0, 630.0],
["2025-07-31", 31.0, 661.0],
["2025-08-31", 31.0, 692.0],
["2025-09-30", 30.0, 722.0],
["2025-10-10", 9.0, 731.0],
]
schedules = [
[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
for d in get_depr_schedule(asset.name, "Draft")
]
self.assertEqual(schedules, expected_schedules)
frappe.db.set_single_value("Accounts Settings", "calculate_depr_using_total_days", 0)
def test_schedule_for_slm_for_existing_asset(self):
asset = create_asset(
calculate_depreciation=1,
depreciation_method="Straight Line",
available_for_use_date="2023-10-10",
is_existing_asset=1,
opening_number_of_booked_depreciations=9,
opening_accumulated_depreciation=265.30,
depreciation_start_date="2024-07-31",
total_number_of_depreciations=24,
frequency_of_depreciation=1,
gross_purchase_amount=731,
)
expected_schedules = [
["2024-07-31", 30.46, 295.76],
["2024-08-31", 30.46, 326.22],
["2024-09-30", 30.46, 356.68],
["2024-10-31", 30.46, 387.14],
["2024-11-30", 30.46, 417.6],
["2024-12-31", 30.46, 448.06],
["2025-01-31", 30.46, 478.52],
["2025-02-28", 30.46, 508.98],
["2025-03-31", 30.46, 539.44],
["2025-04-30", 30.46, 569.9],
["2025-05-31", 30.46, 600.36],
["2025-06-30", 30.46, 630.82],
["2025-07-31", 30.46, 661.28],
["2025-08-31", 30.46, 691.74],
["2025-09-30", 30.46, 722.2],
["2025-10-10", 8.8, 731.0],
]
schedules = [
[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
for d in get_depr_schedule(asset.name, "Draft")
]
self.assertEqual(schedules, expected_schedules)
def test_schedule_sl_method_for_existing_asset_with_frequency_of_3_months(self):
asset = create_asset(
calculate_depreciation=1,
depreciation_method="Straight Line",
available_for_use_date="2023-11-01",
is_existing_asset=1,
opening_number_of_booked_depreciations=4,
opening_accumulated_depreciation=223.15,
depreciation_start_date="2024-12-31",
total_number_of_depreciations=12,
frequency_of_depreciation=3,
gross_purchase_amount=731,
)
expected_schedules = [
["2024-12-31", 60.92, 284.07],
["2025-03-31", 60.92, 344.99],
["2025-06-30", 60.92, 405.91],
["2025-09-30", 60.92, 466.83],
["2025-12-31", 60.92, 527.75],
["2026-03-31", 60.92, 588.67],
["2026-06-30", 60.92, 649.59],
["2026-09-30", 60.92, 710.51],
["2026-11-01", 20.49, 731.0],
]
schedules = [
[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
for d in get_depr_schedule(asset.name, "Draft")
]
self.assertEqual(schedules, expected_schedules)
# Enable Checkbox to Calculate depreciation using total days in depreciation period
def test_daily_prorata_based_depr_after_enabling_configuration(self):
frappe.db.set_single_value("Accounts Settings", "calculate_depr_using_total_days", 1)
asset = create_asset(
calculate_depreciation=1,
depreciation_method="Straight Line",
daily_prorata_based=1,
gross_purchase_amount=1096,
available_for_use_date="2020-01-15",
depreciation_start_date="2020-01-31",
frequency_of_depreciation=1,
total_number_of_depreciations=36,
)
expected_schedule = [
["2020-01-31", 17.0, 17.0],
["2020-02-29", 29.0, 46.0],
["2020-03-31", 31.0, 77.0],
["2020-04-30", 30.0, 107.0],
["2020-05-31", 31.0, 138.0],
["2020-06-30", 30.0, 168.0],
["2020-07-31", 31.0, 199.0],
["2020-08-31", 31.0, 230.0],
["2020-09-30", 30.0, 260.0],
["2020-10-31", 31.0, 291.0],
["2020-11-30", 30.0, 321.0],
["2020-12-31", 31.0, 352.0],
["2021-01-31", 31.0, 383.0],
["2021-02-28", 28.0, 411.0],
["2021-03-31", 31.0, 442.0],
["2021-04-30", 30.0, 472.0],
["2021-05-31", 31.0, 503.0],
["2021-06-30", 30.0, 533.0],
["2021-07-31", 31.0, 564.0],
["2021-08-31", 31.0, 595.0],
["2021-09-30", 30.0, 625.0],
["2021-10-31", 31.0, 656.0],
["2021-11-30", 30.0, 686.0],
["2021-12-31", 31.0, 717.0],
["2022-01-31", 31.0, 748.0],
["2022-02-28", 28.0, 776.0],
["2022-03-31", 31.0, 807.0],
["2022-04-30", 30.0, 837.0],
["2022-05-31", 31.0, 868.0],
["2022-06-30", 30.0, 898.0],
["2022-07-31", 31.0, 929.0],
["2022-08-31", 31.0, 960.0],
["2022-09-30", 30.0, 990.0],
["2022-10-31", 31.0, 1021.0],
["2022-11-30", 30.0, 1051.0],
["2022-12-31", 31.0, 1082.0],
["2023-01-15", 14.0, 1096.0],
]
schedules = [
[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
for d in get_depr_schedule(asset.name, "Draft")
]
self.assertEqual(schedules, expected_schedule)
frappe.db.set_single_value("Accounts Settings", "calculate_depr_using_total_days", 0)
# Test for Written Down Value Method
# Frequency of deprciation = 3
def test_for_daily_prorata_based_depreciation_wdv_method_frequency_3_months(self):

View File

@@ -3,6 +3,15 @@
frappe.ui.form.on("Asset Maintenance", {
setup: (frm) => {
frm.set_query("asset_name", function () {
return {
filters: {
company: frm.doc.company,
docstatus: 1,
},
};
});
frm.set_query("assign_to", "asset_maintenance_tasks", function (doc) {
return {
query: "erpnext.assets.doctype.asset_maintenance.asset_maintenance.get_team_members",

View File

@@ -18,9 +18,7 @@ class AssetMaintenance(Document):
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.assets.doctype.asset_maintenance_task.asset_maintenance_task import (
AssetMaintenanceTask,
)
from erpnext.assets.doctype.asset_maintenance_task.asset_maintenance_task import AssetMaintenanceTask
asset_category: DF.ReadOnly | None
asset_maintenance_tasks: DF.Table[AssetMaintenanceTask]
@@ -47,6 +45,11 @@ class AssetMaintenance(Document):
assign_tasks(self.name, task.assign_to, task.maintenance_task, task.next_due_date)
self.sync_maintenance_tasks()
def after_delete(self):
asset = frappe.get_doc("Asset", self.asset_name)
if asset.status == "In Maintenance":
asset.set_status()
def sync_maintenance_tasks(self):
tasks_names = []
for task in self.get("asset_maintenance_tasks"):

View File

@@ -5,7 +5,8 @@
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import getdate, nowdate
from frappe.query_builder import DocType
from frappe.utils import getdate, nowdate, today
from erpnext.assets.doctype.asset_maintenance.asset_maintenance import calculate_next_due_date
@@ -75,6 +76,17 @@ class AssetMaintenanceLog(Document):
asset_maintenance_doc.save()
def update_asset_maintenance_log_status():
AssetMaintenanceLog = DocType("Asset Maintenance Log")
(
frappe.qb.update(AssetMaintenanceLog)
.set(AssetMaintenanceLog.maintenance_status, "Overdue")
.where(
(AssetMaintenanceLog.maintenance_status == "Planned") & (AssetMaintenanceLog.due_date < today())
)
).run()
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_maintenance_tasks(doctype, txt, searchfield, start, page_len, filters):

View File

@@ -20,14 +20,23 @@ frappe.ui.form.on("Asset Repair", {
};
};
frm.fields_dict.warehouse.get_query = function (doc) {
frm.set_query("asset", function () {
return {
filters: {
company: frm.doc.company,
docstatus: 1,
},
};
});
frm.set_query("warehouse", "stock_items", function () {
return {
filters: {
is_group: 0,
company: doc.company,
company: frm.doc.company,
},
};
};
});
frm.set_query("serial_and_batch_bundle", "stock_items", (doc, cdt, cdn) => {
let row = locals[cdt][cdn];
@@ -79,7 +88,7 @@ frappe.ui.form.on("Asset Repair", {
});
}
if (frm.doc.repair_status == "Completed") {
if (frm.doc.repair_status == "Completed" && !frm.doc.completion_date) {
frm.set_value("completion_date", frappe.datetime.now_datetime());
}
},
@@ -87,15 +96,48 @@ frappe.ui.form.on("Asset Repair", {
stock_items_on_form_rendered() {
erpnext.setup_serial_or_batch_no();
},
stock_consumption: function (frm) {
if (!frm.doc.stock_consumption) {
frm.clear_table("stock_items");
frm.refresh_field("stock_items");
}
},
purchase_invoice: function (frm) {
if (frm.doc.purchase_invoice) {
frappe.call({
method: "frappe.client.get_value",
args: {
doctype: "Purchase Invoice",
fieldname: "base_net_total",
filters: { name: frm.doc.purchase_invoice },
},
callback: function (r) {
if (r.message) {
frm.set_value("repair_cost", r.message.base_net_total);
}
},
});
} else {
frm.set_value("repair_cost", 0);
}
},
});
frappe.ui.form.on("Asset Repair Consumed Item", {
item_code: function (frm, cdt, cdn) {
warehouse: function (frm, cdt, cdn) {
var item = locals[cdt][cdn];
if (!item.item_code) {
frappe.msgprint(__("Please select an item code before setting the warehouse."));
frappe.model.set_value(cdt, cdn, "warehouse", "");
return;
}
let item_args = {
item_code: item.item_code,
warehouse: frm.doc.warehouse,
warehouse: item.warehouse,
qty: item.consumed_quantity,
serial_no: item.serial_no,
company: frm.doc.company,

View File

@@ -22,16 +22,14 @@
"column_break_14",
"project",
"accounting_details",
"repair_cost",
"purchase_invoice",
"capitalize_repair_cost",
"stock_consumption",
"column_break_8",
"purchase_invoice",
"repair_cost",
"stock_consumption_details_section",
"warehouse",
"stock_items",
"total_repair_cost",
"stock_entry",
"asset_depreciation_details_section",
"increase_in_asset_life",
"section_break_9",
@@ -122,7 +120,8 @@
"default": "0",
"fieldname": "repair_cost",
"fieldtype": "Currency",
"label": "Repair Cost"
"label": "Repair Cost",
"read_only": 1
},
{
"fieldname": "amended_from",
@@ -218,13 +217,6 @@
"label": "Total Repair Cost",
"read_only": 1
},
{
"depends_on": "stock_consumption",
"fieldname": "warehouse",
"fieldtype": "Link",
"label": "Warehouse",
"options": "Warehouse"
},
{
"depends_on": "capitalize_repair_cost",
"fieldname": "asset_depreciation_details_section",
@@ -251,20 +243,12 @@
"fieldtype": "Link",
"label": "Company",
"options": "Company"
},
{
"fieldname": "stock_entry",
"fieldtype": "Link",
"label": "Stock Entry",
"no_copy": 1,
"options": "Stock Entry",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2022-08-16 15:55:25.023471",
"modified": "2024-06-13 16:14:14.398356",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Repair",

View File

@@ -47,20 +47,25 @@ class AssetRepair(AccountsController):
repair_cost: DF.Currency
repair_status: DF.Literal["Pending", "Completed", "Cancelled"]
stock_consumption: DF.Check
stock_entry: DF.Link | None
stock_items: DF.Table[AssetRepairConsumedItem]
total_repair_cost: DF.Currency
warehouse: DF.Link | None
# end: auto-generated types
def validate(self):
self.asset_doc = frappe.get_doc("Asset", self.asset)
self.validate_dates()
self.update_status()
if self.get("stock_items"):
self.set_stock_items_cost()
self.calculate_total_repair_cost()
def validate_dates(self):
if self.completion_date and (self.failure_date > self.completion_date):
frappe.throw(
_("Completion Date can not be before Failure Date. Please adjust the dates accordingly.")
)
def update_status(self):
if self.repair_status == "Pending" and self.asset_doc.status != "Out of Order":
frappe.db.set_value("Asset", self.asset, "status", "Out of Order")
@@ -105,22 +110,22 @@ class AssetRepair(AccountsController):
if self.asset_doc.calculate_depreciation and self.increase_in_asset_life:
self.modify_depreciation_schedule()
notes = _(
"This schedule was created when Asset {0} was repaired through Asset Repair {1}."
).format(
get_link_to_form(self.asset_doc.doctype, self.asset_doc.name),
get_link_to_form(self.doctype, self.name),
)
self.asset_doc.flags.ignore_validate_update_after_submit = True
make_new_active_asset_depr_schedules_and_cancel_current_ones(self.asset_doc, notes)
self.asset_doc.save()
notes = _(
"This schedule was created when Asset {0} was repaired through Asset Repair {1}."
).format(
get_link_to_form(self.asset_doc.doctype, self.asset_doc.name),
get_link_to_form(self.doctype, self.name),
)
self.asset_doc.flags.ignore_validate_update_after_submit = True
make_new_active_asset_depr_schedules_and_cancel_current_ones(self.asset_doc, notes)
self.asset_doc.save()
add_asset_activity(
self.asset,
_("Asset updated after completion of Asset Repair {0}").format(
get_link_to_form("Asset Repair", self.name)
),
)
add_asset_activity(
self.asset,
_("Asset updated after completion of Asset Repair {0}").format(
get_link_to_form("Asset Repair", self.name)
),
)
def before_cancel(self):
self.asset_doc = frappe.get_doc("Asset", self.asset)
@@ -136,29 +141,28 @@ class AssetRepair(AccountsController):
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"):
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
self.make_gl_entries(cancel=True)
self.db_set("stock_entry", None)
if self.asset_doc.calculate_depreciation and self.increase_in_asset_life:
self.revert_depreciation_schedule_on_cancellation()
notes = _("This schedule was created when Asset {0}'s Asset Repair {1} was cancelled.").format(
get_link_to_form(self.asset_doc.doctype, self.asset_doc.name),
get_link_to_form(self.doctype, self.name),
)
self.asset_doc.flags.ignore_validate_update_after_submit = True
make_new_active_asset_depr_schedules_and_cancel_current_ones(self.asset_doc, notes)
self.asset_doc.save()
notes = _(
"This schedule was created when Asset {0}'s Asset Repair {1} was cancelled."
).format(
get_link_to_form(self.asset_doc.doctype, self.asset_doc.name),
get_link_to_form(self.doctype, self.name),
)
self.asset_doc.flags.ignore_validate_update_after_submit = True
make_new_active_asset_depr_schedules_and_cancel_current_ones(self.asset_doc, notes)
self.asset_doc.save()
add_asset_activity(
self.asset,
_("Asset updated after cancellation of Asset Repair {0}").format(
get_link_to_form("Asset Repair", self.name)
),
)
add_asset_activity(
self.asset,
_("Asset updated after cancellation of Asset Repair {0}").format(
get_link_to_form("Asset Repair", self.name)
),
)
def after_delete(self):
frappe.get_doc("Asset", self.asset).set_status()
@@ -170,11 +174,6 @@ class AssetRepair(AccountsController):
def check_for_stock_items_and_warehouse(self):
if not self.get("stock_items"):
frappe.throw(_("Please enter Stock Items consumed during the Repair."), title=_("Missing Items"))
if not self.warehouse:
frappe.throw(
_("Please enter Warehouse from which Stock Items consumed during the Repair were taken."),
title=_("Missing Warehouse"),
)
def increase_asset_value(self):
total_value_of_stock_consumed = self.get_total_value_of_stock_consumed()
@@ -208,6 +207,7 @@ class AssetRepair(AccountsController):
stock_entry = frappe.get_doc(
{"doctype": "Stock Entry", "stock_entry_type": "Material Issue", "company": self.company}
)
stock_entry.asset_repair = self.name
for stock_item in self.get("stock_items"):
self.validate_serial_no(stock_item)
@@ -215,7 +215,7 @@ class AssetRepair(AccountsController):
stock_entry.append(
"items",
{
"s_warehouse": self.warehouse,
"s_warehouse": stock_item.warehouse,
"item_code": stock_item.item_code,
"qty": stock_item.consumed_quantity,
"basic_rate": stock_item.valuation_rate,
@@ -228,8 +228,6 @@ class AssetRepair(AccountsController):
stock_entry.insert()
stock_entry.submit()
self.db_set("stock_entry", stock_entry.name)
def validate_serial_no(self, stock_item):
if not stock_item.serial_and_batch_bundle and frappe.get_cached_value(
"Item", stock_item.item_code, "has_serial_no"
@@ -247,12 +245,6 @@ class AssetRepair(AccountsController):
"Serial and Batch Bundle", stock_item.serial_and_batch_bundle, values_to_update
)
def increase_stock_quantity(self):
if self.stock_entry:
stock_entry = frappe.get_doc("Stock Entry", self.stock_entry)
stock_entry.flags.ignore_links = True
stock_entry.cancel()
def make_gl_entries(self, cancel=False):
if flt(self.total_repair_cost) > 0:
gl_entries = self.get_gl_entries()
@@ -316,7 +308,7 @@ class AssetRepair(AccountsController):
return
# creating GL Entries for each row in Stock Items based on the Stock Entry created for it
stock_entry = frappe.get_doc("Stock Entry", self.stock_entry)
stock_entry = frappe.get_doc("Stock Entry", {"asset_repair": self.name})
default_expense_account = None
if not erpnext.is_perpetual_inventory_enabled(self.company):
@@ -357,7 +349,7 @@ class AssetRepair(AccountsController):
"cost_center": self.cost_center,
"posting_date": getdate(),
"against_voucher_type": "Stock Entry",
"against_voucher": self.stock_entry,
"against_voucher": stock_entry.name,
"company": self.company,
},
item=self,

View File

@@ -76,14 +76,14 @@ class TestAssetRepair(unittest.TestCase):
def test_warehouse(self):
asset_repair = create_asset_repair(stock_consumption=1)
self.assertTrue(asset_repair.stock_consumption)
self.assertTrue(asset_repair.warehouse)
self.assertTrue(asset_repair.stock_items[0].warehouse)
def test_decrease_stock_quantity(self):
asset_repair = create_asset_repair(stock_consumption=1, submit=1)
stock_entry = frappe.get_last_doc("Stock Entry")
self.assertEqual(stock_entry.stock_entry_type, "Material Issue")
self.assertEqual(stock_entry.items[0].s_warehouse, asset_repair.warehouse)
self.assertEqual(stock_entry.items[0].s_warehouse, asset_repair.stock_items[0].warehouse)
self.assertEqual(stock_entry.items[0].item_code, asset_repair.stock_items[0].item_code)
self.assertEqual(stock_entry.items[0].qty, asset_repair.stock_items[0].consumed_quantity)
@@ -114,14 +114,14 @@ class TestAssetRepair(unittest.TestCase):
asset_repair.repair_status = "Completed"
self.assertRaises(frappe.ValidationError, asset_repair.submit)
def test_increase_in_asset_value_due_to_stock_consumption(self):
def test_no_increase_in_asset_value_when_not_capitalized(self):
asset = create_asset(calculate_depreciation=1, submit=1)
initial_asset_value = get_asset_value_after_depreciation(asset.name)
asset_repair = create_asset_repair(asset=asset, stock_consumption=1, submit=1)
create_asset_repair(asset=asset, stock_consumption=1, submit=1)
asset.reload()
increase_in_asset_value = get_asset_value_after_depreciation(asset.name) - initial_asset_value
self.assertEqual(asset_repair.stock_items[0].total_value, increase_in_asset_value)
self.assertEqual(increase_in_asset_value, 0)
def test_increase_in_asset_value_due_to_repair_cost_capitalisation(self):
asset = create_asset(calculate_depreciation=1, submit=1)
@@ -185,7 +185,7 @@ class TestAssetRepair(unittest.TestCase):
frappe.get_doc("Purchase Invoice", asset_repair.purchase_invoice).items[0].expense_account
)
stock_entry_expense_account = (
frappe.get_doc("Stock Entry", asset_repair.stock_entry).get("items")[0].expense_account
frappe.get_doc("Stock Entry", {"asset_repair": asset_repair.name}).get("items")[0].expense_account
)
expected_values = {
@@ -260,6 +260,12 @@ class TestAssetRepair(unittest.TestCase):
asset.finance_books[0].value_after_depreciation,
)
def test_asset_repiar_link_in_stock_entry(self):
asset = create_asset(calculate_depreciation=1, submit=1)
asset_repair = create_asset_repair(asset=asset, stock_consumption=1, submit=1)
stock_entry = frappe.get_last_doc("Stock Entry")
self.assertEqual(stock_entry.asset_repair, asset_repair.name)
def num_of_depreciations(asset):
return asset.finance_books[0].total_number_of_depreciations
@@ -289,7 +295,7 @@ def create_asset_repair(**args):
if args.stock_consumption:
asset_repair.stock_consumption = 1
asset_repair.warehouse = args.warehouse or create_warehouse("Test Warehouse", company=asset.company)
warehouse = args.warehouse or create_warehouse("Test Warehouse", company=asset.company)
bundle = None
if args.serial_no:
@@ -297,8 +303,8 @@ def create_asset_repair(**args):
frappe._dict(
{
"item_code": args.item_code,
"warehouse": asset_repair.warehouse,
"company": frappe.get_cached_value("Warehouse", asset_repair.warehouse, "company"),
"warehouse": warehouse,
"company": frappe.get_cached_value("Warehouse", warehouse, "company"),
"qty": (flt(args.stock_qty) or 1) * -1,
"voucher_type": "Asset Repair",
"type_of_transaction": "Asset Repair",
@@ -314,6 +320,7 @@ def create_asset_repair(**args):
"stock_items",
{
"item_code": args.item_code or "_Test Stock Item",
"warehouse": warehouse,
"valuation_rate": args.rate if args.get("rate") is not None else 100,
"consumed_quantity": args.qty or 1,
"serial_and_batch_bundle": bundle,
@@ -333,7 +340,7 @@ def create_asset_repair(**args):
stock_entry.append(
"items",
{
"t_warehouse": asset_repair.warehouse,
"t_warehouse": asset_repair.stock_items[0].warehouse,
"item_code": asset_repair.stock_items[0].item_code,
"qty": asset_repair.stock_items[0].consumed_quantity,
"basic_rate": args.rate if args.get("rate") is not None else 100,
@@ -351,7 +358,7 @@ def create_asset_repair(**args):
company=asset.company,
expense_account=frappe.db.get_value("Company", asset.company, "default_expense_account"),
cost_center=asset_repair.cost_center,
warehouse=asset_repair.warehouse,
warehouse=args.warehouse or create_warehouse("Test Warehouse", company=asset.company),
)
asset_repair.purchase_invoice = pi.name

View File

@@ -6,6 +6,7 @@
"engine": "InnoDB",
"field_order": [
"item_code",
"warehouse",
"valuation_rate",
"consumed_quantity",
"total_value",
@@ -44,19 +45,28 @@
"fieldtype": "Link",
"in_list_view": 1,
"label": "Item",
"options": "Item"
"options": "Item",
"reqd": 1
},
{
"fieldname": "serial_and_batch_bundle",
"fieldtype": "Link",
"label": "Serial and Batch Bundle",
"options": "Serial and Batch Bundle"
},
{
"fieldname": "warehouse",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Warehouse",
"options": "Warehouse",
"reqd": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2023-04-06 02:24:20.375870",
"modified": "2024-06-13 12:01:47.147333",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Repair Consumed Item",

View File

@@ -15,7 +15,7 @@ class AssetRepairConsumedItem(Document):
from frappe.types import DF
consumed_quantity: DF.Data | None
item_code: DF.Link | None
item_code: DF.Link
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
@@ -23,6 +23,7 @@ class AssetRepairConsumedItem(Document):
serial_no: DF.SmallText | None
total_value: DF.Currency
valuation_rate: DF.Currency
warehouse: DF.Link
# end: auto-generated types
pass

View File

@@ -174,7 +174,7 @@
"fieldname": "supplier_type",
"fieldtype": "Select",
"label": "Supplier Type",
"options": "Company\nIndividual\nProprietorship\nPartnership",
"options": "Company\nIndividual\nPartnership",
"reqd": 1
},
{

View File

@@ -65,7 +65,7 @@ class Supplier(TransactionBase):
supplier_name: DF.Data
supplier_primary_address: DF.Link | None
supplier_primary_contact: DF.Link | None
supplier_type: DF.Literal["Company", "Individual", "Proprietorship", "Partnership"]
supplier_type: DF.Literal["Company", "Individual", "Partnership"]
tax_category: DF.Link | None
tax_id: DF.Data | None
tax_withholding_category: DF.Link | None

View File

@@ -43,9 +43,10 @@ def get_data(filters):
query = (
frappe.qb.from_(po)
.from_(po_item)
.inner_join(po_item)
.on(po_item.parent == po.name)
.left_join(pi_item)
.on(pi_item.po_detail == po_item.name)
.on((pi_item.po_detail == po_item.name) & (pi_item.docstatus == 1))
.select(
po.transaction_date.as_("date"),
po_item.schedule_date.as_("required_date"),

View File

@@ -1764,8 +1764,8 @@ class AccountsController(TransactionBase):
item_allowance = {}
global_qty_allowance, global_amount_allowance = None, None
role_allowed_to_over_bill = frappe.db.get_single_value(
"Accounts Settings", "role_allowed_to_over_bill"
role_allowed_to_over_bill = frappe.get_cached_value(
"Accounts Settings", None, "role_allowed_to_over_bill"
)
user_roles = frappe.get_roles()
@@ -2489,16 +2489,12 @@ class AccountsController(TransactionBase):
@frappe.whitelist()
def repost_accounting_entries(self):
if self.repost_required:
repost_ledger = frappe.new_doc("Repost Accounting Ledger")
repost_ledger.company = self.company
repost_ledger.append("vouchers", {"voucher_type": self.doctype, "voucher_no": self.name})
repost_ledger.flags.ignore_permissions = True
repost_ledger.insert()
repost_ledger.submit()
self.db_set("repost_required", 0)
else:
frappe.throw(_("No updates pending for reposting"))
repost_ledger = frappe.new_doc("Repost Accounting Ledger")
repost_ledger.company = self.company
repost_ledger.append("vouchers", {"voucher_type": self.doctype, "voucher_no": self.name})
repost_ledger.flags.ignore_permissions = True
repost_ledger.insert()
repost_ledger.submit()
@frappe.whitelist()

View File

@@ -541,7 +541,9 @@ class BuyingController(SubcontractingController):
"actual_qty": flt(pr_qty),
"serial_and_batch_bundle": (
d.serial_and_batch_bundle
if not self.is_internal_transfer() or self.is_return
if not self.is_internal_transfer()
or self.is_return
or (self.is_internal_transfer() and self.docstatus == 2)
else self.get_package_for_target_warehouse(
d, type_of_transaction=type_of_transaction
)
@@ -580,6 +582,14 @@ class BuyingController(SubcontractingController):
(not cint(self.is_return) and self.docstatus == 2)
or (cint(self.is_return) and self.docstatus == 1)
):
serial_and_batch_bundle = None
if self.is_internal_transfer() and self.docstatus == 2:
serial_and_batch_bundle = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_detail_no": d.name, "warehouse": d.warehouse},
"serial_and_batch_bundle",
)
from_warehouse_sle = self.get_sl_entries(
d,
{
@@ -589,7 +599,7 @@ class BuyingController(SubcontractingController):
"serial_and_batch_bundle": (
self.get_package_for_target_warehouse(d, d.from_warehouse, "Inward")
if self.is_internal_transfer() and self.is_return
else None
else serial_and_batch_bundle
),
},
)

View File

@@ -41,7 +41,8 @@ def get_variant(template, args=None, variant=None, manufacturer=None, manufactur
if isinstance(args, str):
args = json.loads(args)
if not args:
attribute_args = {k: v for k, v in args.items() if k != "use_template_image"}
if not attribute_args:
frappe.throw(_("Please specify at least one attribute in the Attributes table"))
return find_variant(template, args, variant)
@@ -197,7 +198,8 @@ def find_variant(template, args, variant_item_code=None):
@frappe.whitelist()
def create_variant(item, args):
def create_variant(item, args, use_template_image=False):
use_template_image = frappe.parse_json(use_template_image)
if isinstance(args, str):
args = json.loads(args)
@@ -211,13 +213,18 @@ def create_variant(item, args):
variant.set("attributes", variant_attributes)
copy_attributes_to_variant(template, variant)
if use_template_image and template.image:
variant.image = template.image
make_variant_item_code(template.item_code, template.item_name, variant)
return variant
@frappe.whitelist()
def enqueue_multiple_variant_creation(item, args):
def enqueue_multiple_variant_creation(item, args, use_template_image=False):
use_template_image = frappe.parse_json(use_template_image)
# There can be innumerable attribute combinations, enqueue
if isinstance(args, str):
variants = json.loads(args)
@@ -228,27 +235,31 @@ def enqueue_multiple_variant_creation(item, args):
frappe.throw(_("Please do not create more than 500 items at a time"))
return
if total_variants < 10:
return create_multiple_variants(item, args)
return create_multiple_variants(item, args, use_template_image)
else:
frappe.enqueue(
"erpnext.controllers.item_variant.create_multiple_variants",
item=item,
args=args,
use_template_image=use_template_image,
now=frappe.flags.in_test,
)
return "queued"
def create_multiple_variants(item, args):
def create_multiple_variants(item, args, use_template_image=False):
count = 0
if isinstance(args, str):
args = json.loads(args)
template_item = frappe.get_doc("Item", item)
args_set = generate_keyed_value_combinations(args)
for attribute_values in args_set:
if not get_variant(item, args=attribute_values):
variant = create_variant(item, attribute_values)
if use_template_image and template_item.image:
variant.image = template_item.image
variant.save()
count += 1

View File

@@ -640,6 +640,12 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
def update_terms(source_doc, target_doc, source_parent):
target_doc.payment_amount = -source_doc.payment_amount
def item_condition(doc):
if return_against_rejected_qty:
return doc.rejected_qty
return doc.qty
doclist = get_mapped_doc(
doctype,
source_name,
@@ -654,6 +660,7 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
"doctype": doctype + " Item",
"field_map": {"serial_no": "serial_no", "batch_no": "batch_no", "bom": "bom"},
"postprocess": update_item,
"condition": item_condition,
},
"Payment Schedule": {"doctype": "Payment Schedule", "postprocess": update_terms},
},

View File

@@ -28,7 +28,7 @@ class SellingController(StockController):
def validate(self):
super().validate()
self.validate_items()
if not self.get("is_debit_note"):
if not (self.get("is_debit_note") or self.get("is_return")):
self.validate_max_discount()
self.validate_selling_price()
self.set_qty_as_per_stock_uom()
@@ -538,7 +538,9 @@ class SellingController(StockController):
self.make_sl_entries(sl_entries)
def get_sle_for_source_warehouse(self, item_row):
serial_and_batch_bundle = item_row.serial_and_batch_bundle
serial_and_batch_bundle = (
item_row.serial_and_batch_bundle if not self.is_internal_transfer() else None
)
if serial_and_batch_bundle and self.is_internal_transfer() and self.is_return:
if self.docstatus == 1:
serial_and_batch_bundle = self.make_package_for_transfer(

View File

@@ -94,7 +94,10 @@ status_map = {
["To Bill", "eval:self.per_billed == 0 and self.docstatus == 1"],
["Partly Billed", "eval:self.per_billed > 0 and self.per_billed < 100 and self.docstatus == 1"],
["Return Issued", "eval:self.per_returned == 100 and self.docstatus == 1"],
["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"],
[
"Completed",
"eval:(self.per_billed == 100 and self.docstatus == 1) or (self.docstatus == 1 and self.grand_total == 0 and self.per_returned != 100 and self.is_return == 0)",
],
["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed' and self.docstatus != 2"],
],
@@ -554,6 +557,7 @@ class StatusUpdater(Document):
ref_doc.set_status(update=True)
@frappe.request_cache
def get_allowance_for(
item_code,
item_allowance=None,
@@ -583,20 +587,20 @@ def get_allowance_for(
global_amount_allowance,
)
qty_allowance, over_billing_allowance = frappe.db.get_value(
qty_allowance, over_billing_allowance = frappe.get_cached_value(
"Item", item_code, ["over_delivery_receipt_allowance", "over_billing_allowance"]
)
if qty_or_amount == "qty" and not qty_allowance:
if global_qty_allowance is None:
global_qty_allowance = flt(
frappe.db.get_single_value("Stock Settings", "over_delivery_receipt_allowance")
frappe.get_cached_value("Stock Settings", None, "over_delivery_receipt_allowance")
)
qty_allowance = global_qty_allowance
elif qty_or_amount == "amount" and not over_billing_allowance:
if global_amount_allowance is None:
global_amount_allowance = flt(
frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
frappe.get_cached_value("Accounts Settings", None, "over_billing_allowance")
)
over_billing_allowance = global_amount_allowance

View File

@@ -218,7 +218,7 @@ class StockController(AccountsController):
"do_not_submit": True if not via_landed_cost_voucher else False,
}
if row.get("qty") or row.get("consumed_qty"):
if row.get("qty") or row.get("consumed_qty") or row.get("stock_qty"):
self.update_bundle_details(bundle_details, table_name, row)
self.create_serial_batch_bundle(bundle_details, row)

View File

@@ -908,6 +908,7 @@ class SubcontractingController(StockController):
item,
{
"item_code": item.rm_item_code,
"incoming_rate": item.rate if self.is_return else 0,
"warehouse": self.supplier_warehouse,
"actual_qty": -1 * flt(item.consumed_qty, item.precision("consumed_qty")),
"dependant_sle_voucher_detail_no": item.reference_name,

View File

@@ -442,6 +442,7 @@ scheduler_events = {
"erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email",
"erpnext.accounts.utils.auto_create_exchange_rate_revaluation_daily",
"erpnext.accounts.utils.run_ledger_health_checks",
"erpnext.assets.doctype.asset_maintenance_log.asset_maintenance_log.update_asset_maintenance_log_status",
],
"weekly": [
"erpnext.accounts.utils.auto_create_exchange_rate_revaluation_weekly",
@@ -455,6 +456,7 @@ scheduler_events = {
],
"monthly_long": [
"erpnext.accounts.deferred_revenue.process_deferred_accounting",
"erpnext.accounts.utils.auto_create_exchange_rate_revaluation_monthly",
],
}

View File

@@ -212,7 +212,6 @@ erpnext.bom.BomConfigurator = class BomConfigurator extends erpnext.TransactionC
item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item));
refresh_field("stock_qty", item.name, item.parentfield);
this.toggle_conversion_factor(item);
this.frm.events.update_cost(this.frm);
}
}
};

View File

@@ -156,12 +156,12 @@ class BOMCreator(Document):
amount = self.get_raw_material_cost()
self.raw_material_cost = amount
def get_raw_material_cost(self, fg_reference_id=None, amount=0):
if not fg_reference_id:
fg_reference_id = self.name
def get_raw_material_cost(self, fg_item=None, amount=0):
if not fg_item:
fg_item = self.item_code
for row in self.items:
if row.fg_reference_id != fg_reference_id:
if row.fg_item != fg_item:
continue
if not row.is_expandable:
@@ -183,7 +183,7 @@ class BOMCreator(Document):
else:
row.amount = 0.0
row.amount = self.get_raw_material_cost(row.name, row.amount)
row.amount = self.get_raw_material_cost(row.item_code, row.amount)
row.rate = flt(row.amount) / (flt(row.qty) * flt(row.conversion_factor))
amount += flt(row.amount)
@@ -365,6 +365,12 @@ def get_children(doctype=None, parent=None, **kwargs):
return frappe.get_all("BOM Creator Item", fields=fields, filters=query_filters, order_by="idx")
def get_parent_row_no(doc, name):
for row in doc.items:
if row.name == name:
return row.idx
@frappe.whitelist()
def add_item(**kwargs):
if isinstance(kwargs, str):
@@ -375,6 +381,11 @@ def add_item(**kwargs):
doc = frappe.get_doc("BOM Creator", kwargs.parent)
item_info = get_item_details(kwargs.item_code)
parent_row_no = ""
if kwargs.fg_reference_id and doc.name != kwargs.fg_reference_id:
parent_row_no = get_parent_row_no(doc, kwargs.fg_reference_id)
kwargs.update(
{
"uom": item_info.stock_uom,
@@ -383,6 +394,9 @@ def add_item(**kwargs):
}
)
if parent_row_no:
kwargs.update({"parent_row_no": parent_row_no})
doc.append("items", kwargs)
doc.save()

View File

@@ -17,8 +17,6 @@
"column_break_3",
"production_capacity",
"warehouse",
"production_capacity_section",
"parts_per_hour",
"workstation_status_tab",
"status",
"column_break_glcv",
@@ -210,16 +208,6 @@
"label": "Warehouse",
"options": "Warehouse"
},
{
"fieldname": "production_capacity_section",
"fieldtype": "Section Break",
"label": "Production Capacity"
},
{
"fieldname": "parts_per_hour",
"fieldtype": "Float",
"label": "Parts Per Hour"
},
{
"fieldname": "total_working_hours",
"fieldtype": "Float",
@@ -252,7 +240,7 @@
"idx": 1,
"image_field": "on_status_image",
"links": [],
"modified": "2023-11-30 12:43:35.808845",
"modified": "2024-06-20 14:17:13.806609",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Workstation",
@@ -277,4 +265,4 @@
"sort_order": "ASC",
"states": [],
"track_changes": 1
}
}

View File

@@ -55,6 +55,9 @@ class Workstation(Document):
hour_rate_electricity: DF.Currency
hour_rate_labour: DF.Currency
hour_rate_rent: DF.Currency
off_status_image: DF.AttachImage | None
on_status_image: DF.AttachImage | None
plant_floor: DF.Link | None
production_capacity: DF.Int
working_hours: DF.Table[WorkstationWorkingHour]
workstation_name: DF.Data

View File

@@ -254,15 +254,16 @@ erpnext.patches.v13_0.reset_corrupt_defaults
erpnext.patches.v13_0.create_accounting_dimensions_for_asset_repair
erpnext.patches.v15_0.delete_taxjar_doctypes
erpnext.patches.v15_0.delete_ecommerce_doctypes
erpnext.patches.v15_0.create_asset_depreciation_schedules_from_assets
erpnext.patches.v14_0.update_reference_due_date_in_journal_entry
erpnext.patches.v15_0.saudi_depreciation_warning
erpnext.patches.v15_0.delete_saudi_doctypes
erpnext.patches.v14_0.show_loan_management_deprecation_warning
erpnext.patches.v14_0.clear_reconciliation_values_from_singles
execute:frappe.rename_doc("Report", "TDS Payable Monthly", "Tax Withholding Details", force=True)
erpnext.patches.v14_0.update_proprietorship_to_individual
[post_model_sync]
erpnext.patches.v15_0.create_asset_depreciation_schedules_from_assets
execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings')
erpnext.patches.v14_0.update_posting_datetime_and_dropped_indexes #22-02-2024
erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents
@@ -362,8 +363,10 @@ erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2
erpnext.patches.v14_0.set_maintain_stock_for_bom_item
erpnext.patches.v15_0.delete_orphaned_asset_movement_item_records
erpnext.patches.v15_0.fix_debit_credit_in_transaction_currency
erpnext.patches.v15_0.remove_cancelled_asset_capitalization_from_asset
erpnext.patches.v15_0.rename_purchase_receipt_amount_to_purchase_amount
erpnext.patches.v14_0.enable_set_priority_for_pricing_rules #1
erpnext.patches.v15_0.rename_number_of_depreciations_booked_to_opening_booked_depreciations
erpnext.patches.v15_0.update_warehouse_field_in_asset_repair_consumed_item_doctype
erpnext.patches.v15_0.update_asset_repair_field_in_stock_entry
erpnext.patches.v15_0.update_total_number_of_booked_depreciations
erpnext.patches.v15_0.do_not_use_batchwise_valuation

View File

@@ -0,0 +1,7 @@
import frappe
def execute():
for doctype in ["Customer", "Supplier"]:
field = doctype.lower() + "_type"
frappe.db.set_value(doctype, {field: "Proprietorship"}, field, "Individual")

View File

@@ -0,0 +1,15 @@
import frappe
def execute():
valuation_method = frappe.db.get_single_value("Stock Settings", "valuation_method")
if valuation_method in ["FIFO", "LIFO"]:
return
if frappe.get_all("Batch", filters={"use_batchwise_valuation": 1}, limit=1):
return
if frappe.get_all("Item", filters={"has_batch_no": 1, "valuation_method": "FIFO"}, limit=1):
return
frappe.db.set_single_value("Stock Settings", "do_not_use_batchwise_valuation", 1)

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