Compare commits

..

138 Commits

Author SHA1 Message Date
Frappe PR Bot
6a4f06cf4a chore(release): Bumped to Version 15.32.1
## [15.32.1](https://github.com/frappe/erpnext/compare/v15.32.0...v15.32.1) (2024-08-08)

### Bug Fixes

* promotional scheme doctype fields in consitency with pricing rule ([#42432](https://github.com/frappe/erpnext/issues/42432)) ([2aa90fd](2aa90fdc14))
2024-08-08 07:06:38 +00:00
ruthra kumar
dfe0079573 Merge pull request #42674 from frappe/mergify/bp/version-15/pr-42550
fix: promotional scheme doctype fields in consitency with pricing rule (backport #42432) (backport #42550)
2024-08-08 12:35:25 +05:30
Lakshit Jain
a983d65404 chore: resolve conflicts (#42553)
(cherry picked from commit 78eb443614)
2024-08-08 06:43:38 +00:00
Lakshit Jain
2aa90fdc14 fix: promotional scheme doctype fields in consitency with pricing rule (#42432)
* fix: add "round_free_qty" check box in promotional scheme

* fix: add `add_for_price_list` field

* fix: set_query in setup for promotional scheme

---------

(cherry picked from commit 8624aeca54)

# Conflicts:
#	erpnext/accounts/doctype/promotional_scheme_price_discount/promotional_scheme_price_discount.json
#	erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json
(cherry picked from commit 87d8603d1d)
2024-08-08 06:43:38 +00:00
Frappe PR Bot
2e3b2db03b chore(release): Bumped to Version 15.32.0
# [15.32.0](https://github.com/frappe/erpnext/compare/v15.31.5...v15.32.0) (2024-08-07)

### Bug Fixes

* 'undefined' in PL and BS report summary on Consolidated report ([3da7071](3da7071327))
* all warehouse filter for the stock report (backport [#42584](https://github.com/frappe/erpnext/issues/42584)) ([#42604](https://github.com/frappe/erpnext/issues/42604)) ([830b95b](830b95bdcb))
* company filter for filtring tax withheld vouchers ([3ad1f2d](3ad1f2d0d0))
* disable primary action button only when there are no active capitalization ([2d644ac](2d644ac066))
* Discount and taxes in return document should follow the reference document (backport [#41911](https://github.com/frappe/erpnext/issues/41911)) ([#42574](https://github.com/frappe/erpnext/issues/42574)) ([9321408](9321408154))
* do not update item price and last purchase rate for inter transf… (backport [#42616](https://github.com/frappe/erpnext/issues/42616)) ([#42633](https://github.com/frappe/erpnext/issues/42633)) ([59b9b7d](59b9b7dc91))
* german translations ([f27e9f3](f27e9f3089))
* inter transfer delivery note issue with batch (backport [#42552](https://github.com/frappe/erpnext/issues/42552)) ([#42556](https://github.com/frappe/erpnext/issues/42556)) ([97cc308](97cc3082e1))
* min height for rows in sales funnel ([2f81c99](2f81c99143))
* resolved conflict ([#42557](https://github.com/frappe/erpnext/issues/42557)) ([c3293d1](c3293d110c))
* reverse debit credit for party gl entry in payment entry based on negative amount ([#42367](https://github.com/frappe/erpnext/issues/42367)) ([14f9aef](14f9aef55c))
* set currency on change of company considering customer default currency (backport [#42405](https://github.com/frappe/erpnext/issues/42405)) ([#42547](https://github.com/frappe/erpnext/issues/42547)) ([7c8d13c](7c8d13c51a))
* set query filters for sales / purchase tax template on PE ([dc9cf74](dc9cf74be8))
* use get_last_day to get the correct date (backport [#42564](https://github.com/frappe/erpnext/issues/42564)) ([#42598](https://github.com/frappe/erpnext/issues/42598)) ([2de86eb](2de86eb0f4))

### Features

* expiry date column in Available Batch Report (backport [#42628](https://github.com/frappe/erpnext/issues/42628)) ([#42642](https://github.com/frappe/erpnext/issues/42642)) ([d8768c5](d8768c5377))
2024-08-07 09:30:48 +00:00
ruthra kumar
51fa336350 Merge pull request #42649 from frappe/version-15-hotfix
chore: release v15
2024-08-07 14:59:36 +05:30
Khushi Rawat
0a3c0ecea2 Merge pull request #42654 from frappe/mergify/bp/version-15-hotfix/pr-42593
fix: disable primary action button only when there are no active capitalization (backport #42593)
2024-08-06 20:19:11 +05:30
Khushi Rawat
2d644ac066 fix: disable primary action button only when there are no active capitalization
(cherry picked from commit bb877f4a6b)
2024-08-06 13:43:27 +00:00
mergify[bot]
d8768c5377 feat: expiry date column in Available Batch Report (backport #42628) (#42642)
feat: expiry date column in Available Batch Report (#42628)

(cherry picked from commit faff84c6e5)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-06 14:45:12 +05:30
ruthra kumar
65c1387a04 Merge pull request #42646 from frappe/mergify/bp/version-15-hotfix/pr-42592
fix: company filter for filtring tax withheld vouchers (backport #42592)
2024-08-06 13:38:47 +05:30
ljain112
3ad1f2d0d0 fix: company filter for filtring tax withheld vouchers
(cherry picked from commit cfe2ae604b)
2024-08-06 07:47:21 +00:00
ruthra kumar
af31f3b291 Merge pull request #42644 from frappe/mergify/bp/version-15-hotfix/pr-42545
fix: min height for rows in sales funnel (backport #42545)
2024-08-06 13:09:29 +05:30
ljain112
2f81c99143 fix: min height for rows in sales funnel
(cherry picked from commit fd71d8af52)
2024-08-06 07:33:19 +00:00
ruthra kumar
e679b336ef Merge pull request #42638 from frappe/mergify/bp/version-15-hotfix/pr-42636
refactor: posting date should not be considered for ignore filters in GL (backport #42636)
2024-08-06 11:43:46 +05:30
ruthra kumar
82c17632f7 refactor: posting date is not considered for ignore filters in GL
(cherry picked from commit c930f8ba9d)
2024-08-06 05:26:25 +00:00
mergify[bot]
59b9b7dc91 fix: do not update item price and last purchase rate for inter transf… (backport #42616) (#42633)
fix: do not update item price and last purchase rate for inter transf… (#42616)

fix: do not update item price and last purchase rate for inter transfer transaction
(cherry picked from commit c8af544ef3)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-06 08:19:59 +05:30
ruthra kumar
d242d5330c Merge pull request #42626 from frappe/mergify/bp/version-15-hotfix/pr-42555
refactor: date filters should be mandatory in Sales Pipeline Analytics report (backport #42555)
2024-08-05 15:32:17 +05:30
ruthra kumar
05c606b65d refactor(test): use test fixture and supply from and to dates
(cherry picked from commit 4253caf910)
2024-08-05 09:39:30 +00:00
ruthra kumar
52578b03b4 refactor: consider empty-string as Not Assigned
(cherry picked from commit 213b2ba942)
2024-08-05 09:39:30 +00:00
ruthra kumar
8978a54479 refactor: report columns should be based on from and to dates
(cherry picked from commit 751a25c4b7)
2024-08-05 09:39:30 +00:00
ruthra kumar
b44598cb7a refactor: make 'from_date' and 'to_date' mandatory
(cherry picked from commit 3617b41b95)
2024-08-05 09:39:30 +00:00
ruthra kumar
48e05b6110 refactor: date filters should be explicit
(cherry picked from commit 40c166a0a0)
2024-08-05 09:39:30 +00:00
ruthra kumar
3640bc7f88 Merge pull request #42623 from frappe/mergify/bp/version-15-hotfix/pr-42621
refactor(test): clear data before GL report tests (backport #42621)
2024-08-05 14:02:19 +05:30
ruthra kumar
531410820f refactor(test): clear old records on GL report tests
(cherry picked from commit 56620785a0)
2024-08-05 08:15:31 +00:00
ruthra kumar
d08eba7308 Merge pull request #42614 from frappe/mergify/bp/version-15-hotfix/pr-42597
refactor: filter to ignore system generated cr / dr reconciliation journals on general ledger (backport #42597)
2024-08-05 11:34:00 +05:30
ruthra kumar
c1bfa0a31a refactor(test): filter and reconcile concerned vouchers
(cherry picked from commit 9ade269b7a)
2024-08-05 05:44:25 +00:00
ruthra kumar
155ca14b19 test: clear old data
(cherry picked from commit 991069bfbc)
2024-08-05 05:44:25 +00:00
ruthra kumar
3ee5991896 refactor: make use of date filters on ignore filterss
(cherry picked from commit 03f3ab522f)
2024-08-05 05:44:25 +00:00
ruthra kumar
7ec7e11634 test: ignore filter for system generated cr / dr note journals
(cherry picked from commit 3ffac73598)
2024-08-05 05:44:25 +00:00
ruthra kumar
17a6224a3f refactor: ignore system generated cr / dr notes on general ledger
(cherry picked from commit bb8c9b5a58)
2024-08-05 05:44:25 +00:00
ruthra kumar
e9bcb1bbb2 refactor: ignore filter in general ledger for cr / dr notes
(cherry picked from commit 59d5beee20)
2024-08-05 05:44:25 +00:00
Frappe PR Bot
d8080910c6 chore(release): Bumped to Version 15.31.5
## [15.31.5](https://github.com/frappe/erpnext/compare/v15.31.4...v15.31.5) (2024-08-03)

### Bug Fixes

* use get_last_day to get the correct date (backport [#42564](https://github.com/frappe/erpnext/issues/42564)) ([#42602](https://github.com/frappe/erpnext/issues/42602)) ([02a31ca](02a31caa05))
2024-08-03 03:58:31 +00:00
mergify[bot]
02a31caa05 fix: use get_last_day to get the correct date (backport #42564) (#42602)
fix: use get_last_day to get the correct date (#42564)

(cherry picked from commit 5d58eb67a6)

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-08-03 09:27:16 +05:30
mergify[bot]
830b95bdcb fix: all warehouse filter for the stock report (backport #42584) (#42604)
fix: all warehouse filter for the stock report

(cherry picked from commit a4311e345d)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2024-08-03 09:02:25 +05:30
mergify[bot]
2de86eb0f4 fix: use get_last_day to get the correct date (backport #42564) (#42598)
fix: use get_last_day to get the correct date (#42564)

(cherry picked from commit 5d58eb67a6)

Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com>
2024-08-02 17:10:37 +05:30
Raffael Meyer
9616c59ed8 Merge pull request #42581 from barredterra/german-translations-01082024 2024-08-01 16:56:24 +02:00
barredterra
f27e9f3089 fix: german translations 2024-08-01 16:33:19 +02:00
mergify[bot]
9321408154 fix: Discount and taxes in return document should follow the reference document (backport #41911) (#42574)
fix: Discount and taxes in return document should follow the reference document (#41911)

* fix: Discount and taxes in return document should follow the reference document

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

* fix: linter issue

(cherry picked from commit 281198456d)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-08-01 15:45:18 +05:30
Frappe PR Bot
703b9accb8 chore(release): Bumped to Version 15.31.4
## [15.31.4](https://github.com/frappe/erpnext/compare/v15.31.3...v15.31.4) (2024-08-01)

### Bug Fixes

* inter transfer delivery note issue with batch (backport [#42552](https://github.com/frappe/erpnext/issues/42552)) ([#42556](https://github.com/frappe/erpnext/issues/42556)) ([bf0e2b3](bf0e2b3b52))
2024-08-01 09:12:25 +00:00
rohitwaghchaure
aca0662a64 Merge pull request #42571 from frappe/mergify/bp/version-15/pr-42556
fix: inter transfer delivery note issue with batch (backport #42552) (backport #42556)
2024-08-01 14:41:10 +05:30
mergify[bot]
bf0e2b3b52 fix: inter transfer delivery note issue with batch (backport #42552) (#42556)
fix: inter transfer delivery note issue with batch (#42552)

(cherry picked from commit f620ef20ae)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
(cherry picked from commit 97cc3082e1)
2024-08-01 07:53:46 +00:00
mergify[bot]
97cc3082e1 fix: inter transfer delivery note issue with batch (backport #42552) (#42556)
fix: inter transfer delivery note issue with batch (#42552)

(cherry picked from commit f620ef20ae)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2024-08-01 13:22:33 +05:30
ruthra kumar
b7b4de7c8d Merge pull request #42566 from frappe/mergify/bp/version-15-hotfix/pr-42563
fix: set query filters for sales / purchase tax template on PE (backport #42563)
2024-07-31 17:28:56 +05:30
ruthra kumar
dc9cf74be8 fix: set query filters for sales / purchase tax template on PE
(cherry picked from commit 9fe47ac101)
2024-07-31 11:39:12 +00:00
ruthra kumar
4e826fb3e9 Merge pull request #42560 from frappe/mergify/bp/version-15-hotfix/pr-42558
fix: 'undefined' in PL and BS report summary on Consolidated report (backport #42558)
2024-07-31 16:19:29 +05:30
Raffael Meyer
553513c4c6 Merge pull request #42561 from barredterra/backport-translations 2024-07-31 12:48:52 +02:00
barredterra
5cfa2d0692 chore: backport german translations from develop 2024-07-31 12:19:13 +02:00
ruthra kumar
3da7071327 fix: 'undefined' in PL and BS report summary on Consolidated report
(cherry picked from commit dd5a5e4919)
2024-07-31 10:04:00 +00:00
Nabin Hait
c3293d110c fix: resolved conflict (#42557) 2024-07-31 14:54:52 +05:30
Smit Vora
7dc68cabcc Merge pull request #42548 from frappe/mergify/bp/version-15-hotfix/pr-42367
fix: reverse debit credit for party gl entry in payment entry based on negative amount (backport #42367)
2024-07-31 13:17:08 +05:30
mergify[bot]
7c8d13c51a fix: set currency on change of company considering customer default currency (backport #42405) (#42547)
fix: set currency on change of company considering customer default currency (#42405)

(cherry picked from commit 4986f28a89)

# Conflicts:
#	erpnext/public/js/controllers/transaction.js

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2024-07-31 13:02:12 +05:30
Lakshit Jain
14f9aef55c fix: reverse debit credit for party gl entry in payment entry based on negative amount (#42367)
* fix: do not absolute the amount for party gl entries

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

* refactor: reduce nesting of if condition

---------

(cherry picked from commit a694390a12)
2024-07-31 07:16:57 +00:00
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
96 changed files with 4600 additions and 536 deletions

View File

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

View File

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

View File

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

View File

@@ -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,14 +195,10 @@ class JournalEntry(AccountsController):
self.update_booked_depreciation()
def on_update_after_submit(self):
if hasattr(self, "repost_required"):
self.needs_repost = self.check_if_fields_updated(
fields_to_check=[], child_tables={"accounts": []}
)
if self.needs_repost:
self.validate_for_repost()
self.db_set("repost_required", self.needs_repost)
self.repost_accounting_entries()
self.needs_repost = self.check_if_fields_updated(fields_to_check=[], child_tables={"accounts": []})
if self.needs_repost:
self.validate_for_repost()
self.repost_accounting_entries()
def on_cancel(self):
# References for this Journal are removed on the `on_cancel` event in accounts_controller

View File

@@ -165,8 +165,25 @@ frappe.ui.form.on("Payment Entry", {
filters: filters,
};
});
},
frm.set_query("sales_taxes_and_charges_template", function () {
return {
filters: {
company: frm.doc.company,
disabled: false,
},
};
});
frm.set_query("purchase_taxes_and_charges_template", function () {
return {
filters: {
company: frm.doc.company,
disabled: false,
},
};
});
},
refresh: function (frm) {
erpnext.hide_company(frm);
frm.events.hide_unhide_fields(frm);

View File

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

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

@@ -1805,6 +1805,46 @@ class TestPaymentReconciliation(FrappeTestCase):
]
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

@@ -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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -170,7 +170,6 @@
"against_expense_account",
"column_break_63",
"unrealized_profit_loss_account",
"repost_required",
"subscription_section",
"subscription",
"auto_repeat",
@@ -364,7 +363,8 @@
"description": "Once set, this invoice will be on hold till the set date",
"fieldname": "release_date",
"fieldtype": "Date",
"label": "Release Date"
"label": "Release Date",
"search_index": 1
},
{
"fieldname": "cb_17",
@@ -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
@@ -796,19 +795,17 @@ class PurchaseInvoice(BuyingController):
self.process_common_party_accounting()
def on_update_after_submit(self):
if hasattr(self, "repost_required"):
fields_to_check = [
"cash_bank_account",
"write_off_account",
"unrealized_profit_loss_account",
"is_opening",
]
child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.db_set("repost_required", self.needs_repost)
self.repost_accounting_entries()
fields_to_check = [
"cash_bank_account",
"write_off_account",
"unrealized_profit_loss_account",
"is_opening",
]
child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.repost_accounting_entries()
def make_gl_entries(self, gl_entries=None, from_repost=False):
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
@@ -1705,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
@@ -1746,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",
@@ -1762,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

@@ -2014,8 +2014,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
["Service - _TC", 1000, 0.0, nowdate()],
]
check_gl_entries(self, pi.name, expected_gle, nowdate())
pi.load_from_db()
self.assertFalse(pi.repost_required)
@change_settings("Buying Settings", {"supplier_group": None})
def test_purchase_invoice_without_supplier_group(self):

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,11 +477,12 @@ 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: true,
for_validate: for_validate,
},
callback: function (r) {
if (!r.exc) {

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,25 +704,23 @@ class SalesInvoice(SellingController):
data.sales_invoice = sales_invoice
def on_update_after_submit(self):
if hasattr(self, "repost_required"):
fields_to_check = [
"additional_discount_account",
"cash_bank_account",
"account_for_change_amount",
"write_off_account",
"loyalty_redemption_account",
"unrealized_profit_loss_account",
"is_opening",
]
child_tables = {
"items": ("income_account", "expense_account", "discount_account"),
"taxes": ("account_head",),
}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.db_set("repost_required", self.needs_repost)
self.repost_accounting_entries()
fields_to_check = [
"additional_discount_account",
"cash_bank_account",
"account_for_change_amount",
"write_off_account",
"loyalty_redemption_account",
"unrealized_profit_loss_account",
"is_opening",
]
child_tables = {
"items": ("income_account", "expense_account", "discount_account"),
"taxes": ("account_head",),
}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.repost_accounting_entries()
def set_paid_amount(self):
paid_amount = 0.0
@@ -1213,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)
@@ -2226,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

@@ -2952,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":
@@ -369,12 +375,14 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
AND ja.party in %s
AND j.apply_tds = 1
AND j.tax_withholding_category = %s
AND j.company = %s
""",
(
tax_details.from_date,
tax_details.to_date,
tuple(parties),
tax_details.get("tax_withholding_category"),
company,
),
as_dict=1,
)
@@ -387,6 +395,20 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
return vouchers, voucher_wise_amount
def get_payment_entry_vouchers(parties, tax_details, company, party_type="Supplier"):
payment_entry_filters = {
"party_type": party_type,
"party": ("in", parties),
"docstatus": 1,
"apply_tax_withholding_amount": 1,
"posting_date": ["between", (tax_details.from_date, tax_details.to_date)],
"tax_withholding_category": tax_details.get("tax_withholding_category"),
"company": company,
}
return frappe.db.get_all("Payment Entry", filters=payment_entry_filters, pluck="name")
def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type="Supplier"):
"""
Use Payment Ledger to fetch unallocated Advance Payments
@@ -477,6 +499,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
"unallocated_amount": (">", 0),
"posting_date": ["between", (tax_details.from_date, tax_details.to_date)],
"tax_withholding_category": tax_details.get("tax_withholding_category"),
"company": inv.company,
}
field = "sum(tax_withholding_net_total)"

View File

@@ -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

@@ -104,7 +104,7 @@ def get_balance_sheet_data(fiscal_year, companies, columns, filters):
if total_credit:
data.append(total_credit)
report_summary = get_bs_summary(
report_summary, primitive_summary = get_bs_summary(
companies,
asset,
liability,
@@ -175,7 +175,7 @@ def get_profit_loss_data(fiscal_year, companies, columns, filters):
chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss)
report_summary = get_pl_summary(
report_summary, primitive_summary = get_pl_summary(
companies, "", income, expense, net_profit_loss, company_currency, filters, True
)

View File

@@ -209,6 +209,11 @@ frappe.query_reports["General Ledger"] = {
label: __("Ignore Exchange Rate Revaluation Journals"),
fieldtype: "Check",
},
{
fieldname: "ignore_cr_dr_notes",
label: __("Ignore System Generated Credit / Debit Notes"),
fieldtype: "Check",
},
],
};

View File

@@ -236,6 +236,20 @@ def get_conditions(filters):
if err_journals:
filters.update({"voucher_no_not_in": [x[0] for x in err_journals]})
if filters.get("ignore_cr_dr_notes"):
system_generated_cr_dr_journals = frappe.db.get_all(
"Journal Entry",
filters={
"company": filters.get("company"),
"docstatus": 1,
"voucher_type": ("in", ["Credit Note", "Debit Note"]),
"is_system_generated": 1,
},
as_list=True,
)
if system_generated_cr_dr_journals:
filters.update({"voucher_no_not_in": [x[0] for x in system_generated_cr_dr_journals]})
if filters.get("voucher_no_not_in"):
conditions.append("voucher_no not in %(voucher_no_not_in)s")

View File

@@ -2,13 +2,32 @@
# MIT License. See license.txt
import frappe
from frappe import qb
from frappe.tests.utils import FrappeTestCase
from frappe.utils import flt, today
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.general_ledger.general_ledger import execute
from erpnext.controllers.sales_and_purchase_return import make_return_doc
class TestGeneralLedger(FrappeTestCase):
def setUp(self):
self.company = "_Test Company"
self.clear_old_entries()
def clear_old_entries(self):
doctype_list = [
"GL Entry",
"Payment Ledger Entry",
"Sales Invoice",
"Purchase Invoice",
"Payment Entry",
"Journal Entry",
]
for doctype in doctype_list:
qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run()
def test_foreign_account_balance_after_exchange_rate_revaluation(self):
"""
Checks the correctness of balance after exchange rate revaluation
@@ -248,3 +267,68 @@ class TestGeneralLedger(FrappeTestCase):
)
)
self.assertIn(revaluation_jv.name, set([x.voucher_no for x in data]))
def test_ignore_cr_dr_notes_filter(self):
si = create_sales_invoice()
cr_note = make_return_doc(si.doctype, si.name)
cr_note.submit()
pr = frappe.get_doc("Payment Reconciliation")
pr.company = si.company
pr.party_type = "Customer"
pr.party = si.customer
pr.receivable_payable_account = si.debit_to
pr.get_unreconciled_entries()
invoices = [invoice.as_dict() for invoice in pr.invoices if invoice.invoice_number == si.name]
payments = [payment.as_dict() for payment in pr.payments if payment.reference_name == cr_note.name]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.reconcile()
system_generated_journal = frappe.db.get_all(
"Journal Entry",
filters={
"docstatus": 1,
"reference_type": si.doctype,
"reference_name": si.name,
"voucher_type": "Credit Note",
"is_system_generated": True,
},
fields=["name"],
)
self.assertEqual(len(system_generated_journal), 1)
expected = set([si.name, cr_note.name, system_generated_journal[0].name])
# Without ignore_cr_dr_notes
columns, data = execute(
frappe._dict(
{
"company": si.company,
"from_date": si.posting_date,
"to_date": si.posting_date,
"account": [si.debit_to],
"group_by": "Group by Voucher (Consolidated)",
"ignore_cr_dr_notes": False,
}
)
)
actual = set([x.voucher_no for x in data if x.voucher_no])
self.assertEqual(expected, actual)
# Without ignore_cr_dr_notes
expected = set([si.name, cr_note.name])
columns, data = execute(
frappe._dict(
{
"company": si.company,
"from_date": si.posting_date,
"to_date": si.posting_date,
"account": [si.debit_to],
"group_by": "Group by Voucher (Consolidated)",
"ignore_cr_dr_notes": True,
}
)
)
actual = set([x.voucher_no for x in data if x.voucher_no])
self.assertEqual(expected, actual)

View File

@@ -315,8 +315,9 @@ def apply_conditions(query, pi, pii, filters):
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

@@ -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

@@ -1598,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

@@ -188,11 +188,21 @@ frappe.ui.form.on("Asset", {
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
if (frm.doc.is_composite_asset) {
$(".primary-action").prop("hidden", true);
$(".form-message").text("Capitalize this asset to confirm");
frappe.call({
method: "erpnext.assets.doctype.asset.asset.has_active_capitalization",
args: {
asset: frm.doc.name,
},
callback: function (r) {
if (!r.message) {
$(".primary-action").prop("hidden", true);
$(".form-message").text("Capitalize this asset to confirm");
frm.add_custom_button(__("Capitalize Asset"), function () {
frm.trigger("create_asset_capitalization");
frm.add_custom_button(__("Capitalize Asset"), function () {
frm.trigger("create_asset_capitalization");
});
}
},
});
}
}
@@ -775,11 +785,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

@@ -221,7 +221,6 @@
"read_only": 1
},
{
"depends_on": "eval:!doc.is_composite_asset",
"fieldname": "gross_purchase_amount",
"fieldtype": "Currency",
"label": "Gross Purchase Amount",
@@ -580,7 +579,7 @@
"link_fieldname": "target_asset"
}
],
"modified": "2024-07-07 22:27:14.733839",
"modified": "2024-08-01 16:39:09.340973",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",

View File

@@ -267,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"),
)
@@ -1035,6 +1035,14 @@ def get_asset_value_after_depreciation(asset_name, finance_book=None):
return asset.get_value_after_depreciation(finance_book)
@frappe.whitelist()
def has_active_capitalization(asset):
active_capitalizations = frappe.db.count(
"Asset Capitalization", filters={"target_asset": asset, "docstatus": 1}
)
return active_capitalizations > 0
@frappe.whitelist()
def split_asset(asset_name, split_qty):
asset = frappe.get_doc("Asset", asset_name)

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

@@ -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,20 +691,15 @@ 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(
@@ -725,7 +729,16 @@ def get_daily_depr_amount(asset, row, schedule_idx, amount):
)
),
add_days(
get_last_day(add_months(row.depreciation_start_date, -1 * row.frequency_of_depreciation)),
get_last_day(
add_months(
row.depreciation_start_date,
(
row.frequency_of_depreciation
* (asset.opening_number_of_booked_depreciations + 1)
)
* -1,
),
),
1,
),
)
@@ -904,7 +917,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,116 @@ 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)

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

@@ -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

@@ -39,16 +39,14 @@ def validate_filters(filters):
def get_data(filters):
po = frappe.qb.DocType("Purchase Order")
po_item = frappe.qb.DocType("Purchase Order Item")
pi = frappe.qb.DocType("Purchase Invoice")
pi_item = frappe.qb.DocType("Purchase Invoice Item")
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)
.left_join(pi)
.on(pi.name == pi_item.parent)
.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"),
@@ -72,7 +70,6 @@ def get_data(filters):
po_item.name,
)
.where((po_item.parent == po.name) & (po.status.notin(("Stopped", "Closed"))) & (po.docstatus == 1))
.where(pi.docstatus == 1)
.groupby(po_item.name)
.orderby(po.transaction_date)
)

View File

@@ -14,6 +14,9 @@ from erpnext.stock.doctype.item.item import get_last_purchase_details, validate_
def update_last_purchase_rate(doc, is_submit) -> None:
"""updates last_purchase_rate in item table for each item"""
if doc.get("is_internal_supplier"):
return
this_purchase_date = getdate(doc.get("posting_date") or doc.get("transaction_date"))
for d in doc.get("items"):

View File

@@ -85,7 +85,6 @@ force_item_fields = (
"brand",
"stock_uom",
"is_fixed_asset",
"item_tax_rate",
"pricing_rules",
"weight_per_unit",
"weight_uom",
@@ -743,7 +742,6 @@ class AccountsController(TransactionBase):
args["is_subcontracted"] = self.is_subcontracted
ret = get_item_details(args, self, for_validate=for_validate, overwrite_warehouse=False)
for fieldname, value in ret.items():
if item.meta.get_field(fieldname) and value is not None:
if item.get(fieldname) is None or fieldname in force_item_fields:
@@ -753,7 +751,10 @@ class AccountsController(TransactionBase):
fieldname
):
item.set(fieldname, value)
elif fieldname == "item_tax_rate" and not (
self.get("is_return") and self.get("return_against")
):
item.set(fieldname, value)
elif fieldname == "serial_no":
# Ensure that serial numbers are matched against Stock UOM
item_conversion_factor = item.get("conversion_factor") or 1.0
@@ -2489,16 +2490,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

@@ -319,6 +319,8 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
def set_missing_values(source, target):
doc = frappe.get_doc(target)
doc.is_return = 1
doc.ignore_pricing_rule = 1
doc.pricing_rules = []
doc.return_against = source.name
doc.set_warehouse = ""
if doctype == "Sales Invoice" or doctype == "POS Invoice":
@@ -478,6 +480,7 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None, return_agai
def update_item(source_doc, target_doc, source_parent):
target_doc.qty = -1 * source_doc.qty
target_doc.pricing_rules = None
if doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
returned_qty_map = get_returned_qty_map_for_row(
source_parent.name, source_parent.supplier, source_doc.name, doctype
@@ -640,6 +643,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 +663,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

@@ -539,7 +539,9 @@ class SellingController(StockController):
def get_sle_for_source_warehouse(self, item_row):
serial_and_batch_bundle = (
item_row.serial_and_batch_bundle if not self.is_internal_transfer() else None
item_row.serial_and_batch_bundle
if not self.is_internal_transfer() or self.docstatus == 1
else None
)
if serial_and_batch_bundle and self.is_internal_transfer() and self.is_return:
if self.docstatus == 1:

View File

@@ -92,6 +92,9 @@ class calculate_taxes_and_totals:
self.doc.base_tax_withholding_net_total = sum_base_net_amount
def validate_item_tax_template(self):
if self.doc.get("is_return") and self.doc.get("return_against"):
return
for item in self._items:
if item.item_code and item.get("item_tax_template"):
item_doc = frappe.get_cached_doc("Item", item.item_code)
@@ -241,7 +244,6 @@ class calculate_taxes_and_totals:
"tax_fraction_for_current_item",
"grand_total_fraction_for_current_item",
]
if tax.charge_type != "Actual" and not (
self.discount_amount_applied and self.doc.apply_discount_on == "Grand Total"
):

View File

@@ -8,7 +8,7 @@ from itertools import groupby
import frappe
from dateutil.relativedelta import relativedelta
from frappe import _
from frappe.utils import cint, flt
from frappe.utils import cint, flt, getdate
from erpnext.setup.utils import get_exchange_rate
@@ -21,7 +21,15 @@ class SalesPipelineAnalytics:
def __init__(self, filters=None):
self.filters = frappe._dict(filters or {})
def validate_filters(self):
if not self.filters.from_date:
frappe.throw(_("From Date is mandatory"))
if not self.filters.to_date:
frappe.throw(_("To Date is mandatory"))
def run(self):
self.validate_filters()
self.get_columns()
self.get_data()
self.get_chart_data()
@@ -185,7 +193,7 @@ class SalesPipelineAnalytics:
count_or_amount = info.get(based_on)
if self.filters.get("pipeline_by") == "Owner":
if value == "Not Assigned" or value == "[]" or value is None:
if value == "Not Assigned" or value == "[]" or value is None or not value:
assigned_to = ["Not Assigned"]
else:
assigned_to = json.loads(value)
@@ -227,10 +235,9 @@ class SalesPipelineAnalytics:
def get_month_list(self):
month_list = []
current_date = date.today()
month_number = date.today().month
current_date = getdate(self.filters.get("from_date"))
for _month in range(month_number, 13):
while current_date < getdate(self.filters.get("to_date")):
month_list.append(current_date.strftime("%B"))
current_date = current_date + relativedelta(months=1)

View File

@@ -1,19 +1,21 @@
import unittest
import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.crm.report.sales_pipeline_analytics.sales_pipeline_analytics import execute
class TestSalesPipelineAnalytics(unittest.TestCase):
@classmethod
def setUpClass(self):
class TestSalesPipelineAnalytics(FrappeTestCase):
def setUp(self):
frappe.db.delete("Opportunity")
create_company()
create_customer()
create_opportunity()
def test_sales_pipeline_analytics(self):
self.from_date = "2021-01-01"
self.to_date = "2021-12-31"
self.check_for_monthly_and_number()
self.check_for_monthly_and_amount()
self.check_for_quarterly_and_number()
@@ -28,6 +30,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -43,6 +47,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -59,6 +65,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -74,6 +82,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -90,6 +100,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -105,6 +117,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -121,6 +135,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -136,6 +152,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"status": "Open",
"opportunity_type": "Sales",
"company": "Best Test",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)
@@ -153,8 +171,8 @@ class TestSalesPipelineAnalytics(unittest.TestCase):
"opportunity_type": "Sales",
"company": "Best Test",
"opportunity_source": "Cold Calling",
"from_date": "2021-08-01",
"to_date": "2021-08-31",
"from_date": self.from_date,
"to_date": self.to_date,
}
report = execute(filters)

View File

@@ -456,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

@@ -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

@@ -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

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

@@ -534,6 +534,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
quotation_to: me.frm.doc.quotation_to,
supplier: me.frm.doc.supplier,
currency: me.frm.doc.currency,
is_internal_supplier: me.frm.doc.is_internal_supplier,
is_internal_customer: me.frm.doc.is_internal_customer,
update_stock: update_stock,
conversion_rate: me.frm.doc.conversion_rate,
price_list: me.frm.doc.selling_price_list || me.frm.doc.buying_price_list,
@@ -826,47 +828,76 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
var me = this;
var set_pricing = function() {
if(me.frm.doc.company && me.frm.fields_dict.currency) {
var company_currency = me.get_company_currency();
var company_doc = frappe.get_doc(":Company", me.frm.doc.company);
if (!me.frm.doc.currency) {
me.frm.set_value("currency", company_currency);
}
if (me.frm.doc.currency == company_currency) {
me.frm.set_value("conversion_rate", 1.0);
}
if (me.frm.doc.price_list_currency == company_currency) {
me.frm.set_value('plc_conversion_rate', 1.0);
}
if (company_doc){
if (company_doc.default_letter_head) {
if(me.frm.fields_dict.letter_head) {
me.frm.set_value("letter_head", company_doc.default_letter_head);
}
}
let selling_doctypes_for_tc = ["Sales Invoice", "Quotation", "Sales Order", "Delivery Note"];
if (company_doc.default_selling_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") &&
selling_doctypes_for_tc.includes(me.frm.doc.doctype) && !me.frm.doc.tc_name) {
me.frm.set_value("tc_name", company_doc.default_selling_terms);
}
let buying_doctypes_for_tc = ["Request for Quotation", "Supplier Quotation", "Purchase Order",
"Material Request", "Purchase Receipt"];
// Purchase Invoice is excluded as per issue #3345
if (company_doc.default_buying_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") &&
buying_doctypes_for_tc.includes(me.frm.doc.doctype) && !me.frm.doc.tc_name) {
me.frm.set_value("tc_name", company_doc.default_buying_terms);
}
}
frappe.run_serially([
() => me.frm.script_manager.trigger("currency"),
() => get_party_currency(),
() => me.update_item_tax_map(),
() => me.apply_default_taxes(),
() => me.apply_pricing_rule()
() => me.apply_pricing_rule(),
() => set_terms(),
() => set_letter_head(),
]);
}
}
var get_party_currency = function() {
var party_type = frappe.meta.has_field(me.frm.doc.doctype, "customer") ? "Customer" : "Supplier";
var party_name = me.frm.doc[party_type.toLowerCase()];
if (party_name) {
frappe.call({
method: "frappe.client.get_value",
args: {
doctype: party_type,
filters: { name: party_name },
fieldname: "default_currency",
},
callback: function (r) {
if (r.message) {
set_currency(r.message.default_currency);
}
}
})
} else {
set_currency();
}
}
var set_currency = function(party_default_currency) {
var company_currency = me.get_company_currency();
var currency = party_default_currency || company_currency;
if (me.frm.doc.currency != currency) {
me.frm.set_value("currency", currency);
}
if (me.frm.doc.currency == company_currency) {
me.frm.set_value("conversion_rate", 1.0);
}
if (me.frm.doc.price_list_currency == company_currency) {
me.frm.set_value('plc_conversion_rate', 1.0);
}
me.frm.script_manager.trigger("currency");
}
var set_terms = function() {
if (frappe.meta.has_field(me.frm.doc.doctype, "tc_name") && !me.frm.doc.tc_name) {
var company_doc = frappe.get_doc(":Company", me.frm.doc.company);
var selling_doctypes = ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"];
var company_terms_fieldname = selling_doctypes.includes(me.frm.doc.doctype) ? "default_selling_terms" : "default_buying_terms";
if (company_doc && company_doc[company_terms_fieldname]) {
me.frm.set_value("tc_name", company_doc[company_terms_fieldname]);
}
}
}
var set_letter_head = function() {
if(me.frm.fields_dict.letter_head) {
var company_doc = frappe.get_doc(":Company", me.frm.doc.company);
if (company_doc && company_doc.default_letter_head) {
me.frm.set_value("letter_head", company_doc.default_letter_head);
}
}
}
var set_party_account = function(set_pricing) {
if (["Sales Invoice", "Purchase Invoice"].includes(me.frm.doc.doctype)) {
if(me.frm.doc.doctype=="Sales Invoice") {
@@ -1621,7 +1652,9 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
"update_stock": ['Sales Invoice', 'Purchase Invoice'].includes(me.frm.doc.doctype) ? cint(me.frm.doc.update_stock) : 0,
"conversion_factor": me.frm.doc.conversion_factor,
"pos_profile": me.frm.doc.doctype == 'Sales Invoice' ? me.frm.doc.pos_profile : '',
"coupon_code": me.frm.doc.coupon_code
"coupon_code": me.frm.doc.coupon_code,
"is_internal_supplier": me.frm.doc.is_internal_supplier,
"is_internal_customer": me.frm.doc.is_internal_customer,
};
}
@@ -1781,6 +1814,12 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
apply_price_list(item, reset_plc_conversion) {
// We need to reset plc_conversion_rate sometimes because the call to
// `erpnext.stock.get_item_details.apply_price_list` is sensitive to its value
if (this.frm.doc.doctype === "Material Request") {
return;
}
if (!reset_plc_conversion) {
this.frm.set_value("plc_conversion_rate", "");
}
@@ -1796,7 +1835,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
me.in_apply_price_list = true;
return this.frm.call({
method: "erpnext.stock.get_item_details.apply_price_list",
args: { args: args },
args: { args: args, doc: me.frm.doc },
callback: function(r) {
if (!r.exc) {
frappe.run_serially([
@@ -1943,6 +1982,8 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
let item_rates = {};
let item_tax_templates = {};
if (me.frm.doc.is_return && me.frm.doc.return_against) return;
$.each(this.frm.doc.items || [], function(i, item) {
if (item.item_code) {
// Use combination of name and item code in case same item is added multiple times

View File

@@ -134,7 +134,7 @@
"label": "Customer Type",
"oldfieldname": "customer_type",
"oldfieldtype": "Select",
"options": "Company\nIndividual\nProprietorship\nPartnership",
"options": "Company\nIndividual\nPartnership",
"reqd": 1
},
{

View File

@@ -52,7 +52,7 @@ class Customer(TransactionBase):
customer_pos_id: DF.Data | None
customer_primary_address: DF.Link | None
customer_primary_contact: DF.Link | None
customer_type: DF.Literal["Company", "Individual", "Proprietorship", "Partnership"]
customer_type: DF.Literal["Company", "Individual", "Partnership"]
default_bank_account: DF.Link | None
default_commission_rate: DF.Float
default_currency: DF.Link | None

View File

@@ -583,6 +583,7 @@ erpnext.PointOfSale.Controller = class {
new_item["serial_no"] = serial_no;
}
new_item["use_serial_batch_fields"] = 1;
if (field === "serial_no") new_item["qty"] = value.split(`\n`).length || 0;
item_row = this.frm.add_child("items", new_item);

View File

@@ -101,7 +101,8 @@ erpnext.PointOfSale.ItemDetails = class {
const serialized = item_row.has_serial_no;
const batched = item_row.has_batch_no;
const no_bundle_selected = !item_row.serial_and_batch_bundle;
const no_bundle_selected =
!item_row.serial_and_batch_bundle && !item_row.serial_no && !item_row.batch_no;
if ((serialized && no_bundle_selected) || (batched && no_bundle_selected)) {
frappe.show_alert({
@@ -403,6 +404,7 @@ erpnext.PointOfSale.ItemDetails = class {
frappe.model.set_value(item_row.doctype, item_row.name, {
serial_and_batch_bundle: r.name,
qty: Math.abs(r.total_qty),
use_serial_batch_fields: 0,
});
}
});

View File

@@ -193,6 +193,9 @@ erpnext.SalesFunnel = class SalesFunnel {
this.options.width = ($(this.elements.funnel_wrapper).width() * 2.0) / 3.0;
this.options.height = (Math.sqrt(3) * this.options.width) / 2.0;
const min_height = (this.options.height * 0.1) / this.options.data.length;
const height = this.options.height * 0.9;
// calculate total weightage
// as height decreases, area decreases by the square of the reduction
// hence, compensating by squaring the index value
@@ -202,7 +205,7 @@ erpnext.SalesFunnel = class SalesFunnel {
// calculate height for each data
$.each(this.options.data, function (i, d) {
d.height = (me.options.height * d.value * Math.pow(i + 1, 2)) / me.options.total_weightage;
d.height = (height * d.value * Math.pow(i + 1, 2)) / me.options.total_weightage + min_height;
});
this.elements.canvas = $("<canvas></canvas>")

View File

@@ -754,7 +754,7 @@
"fieldname": "auto_err_frequency",
"fieldtype": "Select",
"label": "Frequency",
"options": "Daily\nWeekly"
"options": "Daily\nWeekly\nMonthly"
},
{
"default": "0",
@@ -808,7 +808,7 @@
"image_field": "company_logo",
"is_tree": 1,
"links": [],
"modified": "2024-06-21 17:46:25.567565",
"modified": "2024-07-24 18:17:56.413971",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",

View File

@@ -31,7 +31,7 @@ class Company(NestedSet):
accumulated_depreciation_account: DF.Link | None
allow_account_creation_against_child_company: DF.Check
asset_received_but_not_billed: DF.Link | None
auto_err_frequency: DF.Literal["Daily", "Weekly"]
auto_err_frequency: DF.Literal["Daily", "Weekly", "Monthly"]
auto_exchange_rate_revaluation: DF.Check
book_advance_payments_in_separate_party_account: DF.Check
capital_work_in_progress_account: DF.Link | None

View File

@@ -163,7 +163,7 @@ def make_taxes_and_charges_template(company_name, doctype, template):
doc.flags.ignore_links = True
doc.flags.ignore_validate = True
doc.flags.ignore_mandatory = True
doc.insert(ignore_permissions=True)
doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
return doc
@@ -196,7 +196,7 @@ def make_item_tax_template(company_name, template):
# Ingone validations to make doctypes faster
doc.flags.ignore_links = True
doc.flags.ignore_validate = True
doc.insert(ignore_permissions=True)
doc.insert(ignore_permissions=True, ignore_if_duplicate=True)
return doc
@@ -233,7 +233,7 @@ def get_or_create_account(company_name, account):
doc = frappe.get_doc(account)
doc.flags.ignore_links = True
doc.flags.ignore_validate = True
doc.insert(ignore_permissions=True, ignore_mandatory=True)
doc.insert(ignore_permissions=True, ignore_mandatory=True, ignore_if_duplicate=True)
return doc

View File

@@ -6,7 +6,7 @@ import json
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days, cstr, flt, nowdate, nowtime, today
from frappe.utils import add_days, cstr, flt, getdate, nowdate, nowtime, today
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.accounts.utils import get_balance_on
@@ -2005,6 +2005,40 @@ class TestDeliveryNote(FrappeTestCase):
self.assertRaises(frappe.ValidationError, dn5.submit)
def test_warranty_expiry_date_for_serial_item(self):
item_code = make_item(
"Test Warranty Expiry Date Item",
properties={
"has_serial_no": 1,
"serial_no_series": "TWE.#####",
"is_stock_item": 1,
"warranty_period": 100,
},
).name
se = make_stock_entry(
item_code=item_code,
target="_Test Warehouse - _TC",
qty=2,
basic_rate=50,
posting_date=nowdate(),
)
serial_nos = get_serial_nos_from_bundle(se.items[0].serial_and_batch_bundle)
create_delivery_note(
item_code=item_code,
qty=2,
rate=300,
use_serial_batch_fields=0,
serial_no=serial_nos,
)
for row in serial_nos:
sn = frappe.get_doc("Serial No", row)
self.assertEqual(getdate(sn.warranty_expiry_date), getdate(add_days(nowdate(), 100)))
self.assertEqual(sn.status, "Delivered")
self.assertEqual(sn.warranty_period, 100)
def create_delivery_note(**args):
dn = frappe.new_doc("Delivery Note")

View File

@@ -1886,9 +1886,19 @@ class TestPurchaseReceipt(FrappeTestCase):
rate=100,
rejected_qty=2,
rejected_warehouse=rejected_warehouse,
do_not_save=1,
)
pr.append(
"items",
{"item_code": item_code, "qty": 2, "rate": 100, "warehouse": warehouse, "rejected_qty": 0},
)
pr.save()
pr.submit()
self.assertEqual(len(pr.items), 2)
pr_return = make_purchase_return_against_rejected_warehouse(pr.name)
self.assertEqual(len(pr_return.items), 1)
self.assertEqual(pr_return.items[0].warehouse, rejected_warehouse)
self.assertEqual(pr_return.items[0].qty, 2.0 * -1)
self.assertEqual(pr_return.items[0].rejected_qty, 0.0)
@@ -3466,6 +3476,122 @@ class TestPurchaseReceipt(FrappeTestCase):
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
def test_internal_transfer_for_batch_items_with_cancel_use_serial_batch_fields(self):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
frappe.db.set_single_value("Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 0)
prepare_data_for_internal_transfer()
customer = "_Test Internal Customer 2"
company = "_Test Company with perpetual inventory"
batch_item_doc = make_item(
"_Test Batch Item For Stock Transfer Cancel Case 11",
{"has_batch_no": 1, "create_new_batch": 1, "batch_number_series": "USBF11-BT-CANBIFST-.####"},
)
serial_item_doc = make_item(
"_Test Serial No Item For Stock Transfer Cancel Case 11",
{"has_serial_no": 1, "serial_no_series": "USBF11-BT-CANBIFST-.####"},
)
inward_entry = make_purchase_receipt(
item_code=batch_item_doc.name,
qty=10,
rate=150,
warehouse="Stores - TCP1",
company="_Test Company with perpetual inventory",
use_serial_batch_fields=1,
do_not_submit=1,
)
inward_entry.append(
"items",
{
"item_code": serial_item_doc.name,
"qty": 15,
"rate": 250,
"item_name": serial_item_doc.item_name,
"conversion_factor": 1.0,
"uom": serial_item_doc.stock_uom,
"stock_uom": serial_item_doc.stock_uom,
"warehouse": "Stores - TCP1",
"use_serial_batch_fields": 1,
},
)
inward_entry.submit()
inward_entry.reload()
for row in inward_entry.items:
self.assertTrue(row.serial_and_batch_bundle)
inter_transfer_dn = create_delivery_note(
item_code=inward_entry.items[0].item_code,
company=company,
customer=customer,
cost_center="Main - TCP1",
expense_account="Cost of Goods Sold - TCP1",
qty=10,
rate=500,
warehouse="Stores - TCP1",
target_warehouse="Work In Progress - TCP1",
batch_no=get_batch_from_bundle(inward_entry.items[0].serial_and_batch_bundle),
use_serial_batch_fields=1,
do_not_submit=1,
)
inter_transfer_dn.append(
"items",
{
"item_code": serial_item_doc.name,
"qty": 15,
"rate": 350,
"item_name": serial_item_doc.item_name,
"conversion_factor": 1.0,
"uom": serial_item_doc.stock_uom,
"stock_uom": serial_item_doc.stock_uom,
"warehouse": "Stores - TCP1",
"target_warehouse": "Work In Progress - TCP1",
"serial_no": "\n".join(
get_serial_nos_from_bundle(inward_entry.items[1].serial_and_batch_bundle)
),
"use_serial_batch_fields": 1,
},
)
inter_transfer_dn.submit()
inter_transfer_dn.reload()
for row in inter_transfer_dn.items:
if row.item_code == batch_item_doc.name:
self.assertEqual(row.rate, 150.0)
else:
self.assertEqual(row.rate, 250.0)
self.assertTrue(row.serial_and_batch_bundle)
inter_transfer_pr = make_inter_company_purchase_receipt(inter_transfer_dn.name)
for row in inter_transfer_pr.items:
row.from_warehouse = "Work In Progress - TCP1"
row.warehouse = "Stores - TCP1"
inter_transfer_pr.submit()
for row in inter_transfer_pr.items:
if row.item_code == batch_item_doc.name:
self.assertEqual(row.rate, 150.0)
else:
self.assertEqual(row.rate, 250.0)
self.assertTrue(row.serial_and_batch_bundle)
inter_transfer_pr.cancel()
inter_transfer_dn.cancel()
frappe.db.set_single_value("Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 1)
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier

View File

@@ -242,6 +242,9 @@ class QualityInspection(Document):
# numeric readings
for i in range(1, 11):
field = "reading_" + str(i)
if reading.get(field) is None:
continue
data[field] = parse_float(reading.get(field))
data["mean"] = self.calculate_mean(reading)

View File

@@ -92,8 +92,10 @@ class SerialandBatchBundle(Document):
if self.type_of_transaction == "Maintenance":
return
self.validate_serial_nos_duplicate()
self.check_future_entries_exists()
if not self.flags.ignore_validate_serial_batch or frappe.flags.in_test:
self.validate_serial_nos_duplicate()
self.check_future_entries_exists()
self.set_is_outward()
self.calculate_total_qty()
self.set_warehouse()
@@ -340,6 +342,9 @@ class SerialandBatchBundle(Document):
rate = frappe.db.get_value(child_table, self.voucher_detail_no, valuation_field)
for d in self.entries:
if (d.incoming_rate == rate) and d.qty and d.stock_value_difference:
continue
d.incoming_rate = flt(rate, precision)
if d.qty:
d.stock_value_difference = flt(d.qty) * flt(d.incoming_rate)
@@ -393,32 +398,6 @@ class SerialandBatchBundle(Document):
self.calculate_qty_and_amount(save=True)
self.validate_quantity(row, qty_field=qty_field)
self.set_warranty_expiry_date()
def set_warranty_expiry_date(self):
if self.type_of_transaction != "Outward":
return
if not (self.docstatus == 1 and self.voucher_type == "Delivery Note" and self.has_serial_no):
return
warranty_period = frappe.get_cached_value("Item", self.item_code, "warranty_period")
if not warranty_period:
return
warranty_expiry_date = add_days(self.posting_date, cint(warranty_period))
serial_nos = self.get_serial_nos()
if not serial_nos:
return
sn_table = frappe.qb.DocType("Serial No")
(
frappe.qb.update(sn_table)
.set(sn_table.warranty_expiry_date, warranty_expiry_date)
.where(sn_table.name.isin(serial_nos))
).run()
def validate_voucher_no(self):
if not (self.voucher_type and self.voucher_no):
@@ -867,6 +846,9 @@ class SerialandBatchBundle(Document):
self.validate_serial_nos_inventory()
def set_purchase_document_no(self):
if self.flags.ignore_validate_serial_batch:
return
if not self.has_serial_no:
return
@@ -935,6 +917,9 @@ class SerialandBatchBundle(Document):
self.validate_voucher_no_docstatus()
def validate_voucher_no_docstatus(self):
if self.voucher_type == "POS Invoice":
return
if frappe.db.get_value(self.voucher_type, self.voucher_no, "docstatus") == 1:
msg = f"""The {self.voucher_type} {bold(self.voucher_no)}
is in submitted state, please cancel it first"""
@@ -1098,7 +1083,9 @@ def create_serial_nos(item_code, serial_nos):
def make_serial_nos(item_code, serial_nos):
item = frappe.get_cached_value("Item", item_code, ["description", "item_code"], as_dict=1)
item = frappe.get_cached_value(
"Item", item_code, ["description", "item_code", "item_name", "warranty_period"], as_dict=1
)
serial_nos = [d.get("serial_no") for d in serial_nos if d.get("serial_no")]
existing_serial_nos = frappe.get_all("Serial No", filters={"name": ("in", serial_nos)})
@@ -1123,6 +1110,7 @@ def make_serial_nos(item_code, serial_nos):
item.item_code,
item.item_name,
item.description,
item.warranty_period or 0,
"Inactive",
)
)
@@ -1137,6 +1125,7 @@ def make_serial_nos(item_code, serial_nos):
"item_code",
"item_name",
"description",
"warranty_period",
"status",
]
@@ -1718,6 +1707,7 @@ def get_reserved_batches_for_pos(kwargs) -> dict:
"`tabPOS Invoice Item`.warehouse",
"`tabPOS Invoice Item`.name as child_docname",
"`tabPOS Invoice`.name as parent_docname",
"`tabPOS Invoice Item`.use_serial_batch_fields",
"`tabPOS Invoice Item`.serial_and_batch_bundle",
],
filters=[
@@ -1731,7 +1721,7 @@ def get_reserved_batches_for_pos(kwargs) -> dict:
ids = [
pos_invoice.serial_and_batch_bundle
for pos_invoice in pos_invoices
if pos_invoice.serial_and_batch_bundle
if pos_invoice.serial_and_batch_bundle and not pos_invoice.use_serial_batch_fields
]
if ids:
@@ -2180,6 +2170,8 @@ def get_stock_ledgers_for_serial_nos(kwargs):
def get_stock_ledgers_batches(kwargs):
from erpnext.stock.utils import get_combine_datetime
stock_ledger_entry = frappe.qb.DocType("Stock Ledger Entry")
batch_table = frappe.qb.DocType("Batch")
@@ -2206,6 +2198,19 @@ def get_stock_ledgers_batches(kwargs):
else:
query = query.where(stock_ledger_entry[field] == kwargs.get(field))
if kwargs.get("posting_date"):
if kwargs.get("posting_time") is None:
kwargs.posting_time = nowtime()
timestamp_condition = stock_ledger_entry.posting_datetime <= get_combine_datetime(
kwargs.posting_date, kwargs.posting_time
)
query = query.where(timestamp_condition)
if kwargs.get("ignore_voucher_nos"):
query = query.where(stock_ledger_entry.voucher_no.notin(kwargs.get("ignore_voucher_nos")))
if kwargs.based_on == "LIFO":
query = query.orderby(batch_table.creation, order=frappe.qb.desc)
elif kwargs.based_on == "Expiry":

View File

@@ -83,7 +83,8 @@
"job_card",
"amended_from",
"credit_note",
"is_return"
"is_return",
"tab_connections"
],
"fields": [
{
@@ -683,6 +684,12 @@
"label": "Asset Repair",
"options": "Asset Repair",
"read_only": 1
},
{
"fieldname": "tab_connections",
"fieldtype": "Tab Break",
"label": "Connections",
"show_dashboard": 1
}
],
"icon": "fa fa-file-text",

View File

@@ -2354,17 +2354,22 @@ class StockEntry(StockController):
return [d.item_code for d in job_card_items]
def add_to_stock_entry_detail(self, item_dict, bom_no=None):
precision = frappe.get_precision("Stock Entry Detail", "qty")
for d in item_dict:
item_row = item_dict[d]
stock_uom = item_row.get("stock_uom") or frappe.db.get_value("Item", d, "stock_uom")
child_qty = flt(item_row["qty"], precision)
if not self.is_return and child_qty <= 0:
continue
se_child = self.append("items")
stock_uom = item_row.get("stock_uom") or frappe.db.get_value("Item", d, "stock_uom")
se_child.s_warehouse = item_row.get("from_warehouse")
se_child.t_warehouse = item_row.get("to_warehouse")
se_child.item_code = item_row.get("item_code") or cstr(d)
se_child.uom = item_row["uom"] if item_row.get("uom") else stock_uom
se_child.stock_uom = stock_uom
se_child.qty = flt(item_row["qty"], se_child.precision("qty"))
se_child.qty = child_qty
se_child.allow_alternative_item = item_row.get("allow_alternative_item", 0)
se_child.subcontracted_item = item_row.get("main_item_code")
se_child.cost_center = item_row.get("cost_center") or get_default_cost_center(

View File

@@ -0,0 +1,26 @@
from frappe import _
# Todo: non_standard_fieldnames is to be decided
def get_data():
return {
"fieldname": "stock_entry",
"non_standard_fieldnames": {
# "DocType Name": "Reference field name",
},
"internal_links": {
"Purchase Order": ["items", "purchase_order"],
"Subcontracting Order": ["items", "subcontracting_order"],
"Subcontracting Receipt": ["items", "subcontracting_receipt"],
},
"transactions": [
{
"label": _("Reference"),
"items": [
"Purchase Order",
"Subcontracting Order",
"Subcontracting Receipt",
],
},
],
}

View File

@@ -16,7 +16,7 @@ from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle impor
get_available_serial_nos,
)
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
from erpnext.stock.utils import get_stock_balance
from erpnext.stock.utils import get_incoming_rate, get_stock_balance
class OpeningEntryAccountError(frappe.ValidationError):
@@ -952,14 +952,21 @@ class StockReconciliation(StockController):
precesion = row.precision("current_qty")
if flt(current_qty, precesion) != flt(row.current_qty, precesion):
if not row.serial_no:
val_rate = get_valuation_rate(
row.item_code,
row.warehouse,
self.doctype,
self.name,
company=self.company,
batch_no=row.batch_no,
serial_and_batch_bundle=row.current_serial_and_batch_bundle,
val_rate = get_incoming_rate(
frappe._dict(
{
"item_code": row.item_code,
"warehouse": row.warehouse,
"qty": current_qty * -1,
"serial_and_batch_bundle": row.current_serial_and_batch_bundle,
"batch_no": row.batch_no,
"voucher_type": self.doctype,
"voucher_no": self.name,
"company": self.company,
"posting_date": self.posting_date,
"posting_time": self.posting_time,
}
)
)
row.current_valuation_rate = val_rate

View File

@@ -4,6 +4,7 @@
# ERPNext - web based ERP (http://erpnext.com)
# For license information, please see license.txt
import json
import frappe
from frappe.tests.utils import FrappeTestCase, change_settings
@@ -1182,6 +1183,98 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
self.assertAlmostEqual(row.incoming_rate, 1000.00)
self.assertEqual(row.serial_no, serial_nos[row.idx - 1])
def test_stock_reco_with_legacy_batch(self):
from erpnext.stock.doctype.batch.batch import get_batch_qty
batch_item_code = self.make_item(
"Test Batch Item Legacy Batch 1",
{
"is_stock_item": 1,
"has_batch_no": 1,
"create_new_batch": 1,
"batch_number_series": "BH1-NRALL-S-.###",
},
).name
warehouse = "_Test Warehouse - _TC"
frappe.flags.ignore_serial_batch_bundle_validation = True
frappe.flags.use_serial_and_batch_fields = True
batch_id = "BH1-NRALL-S-0001"
if not frappe.db.exists("Batch", batch_id):
batch_doc = frappe.get_doc(
{
"doctype": "Batch",
"batch_id": batch_id,
"item": batch_item_code,
"use_batchwise_valuation": 0,
}
).insert(ignore_permissions=True)
self.assertTrue(batch_doc.use_batchwise_valuation)
stock_queue = []
qty_after_transaction = 0
balance_value = 0
i = 0
for qty, valuation in {10: 100, 20: 200}.items():
i += 1
stock_queue.append([qty, valuation])
qty_after_transaction += qty
balance_value += qty_after_transaction * valuation
doc = frappe.get_doc(
{
"doctype": "Stock Ledger Entry",
"posting_date": add_days(nowdate(), -2 * i),
"posting_time": nowtime(),
"batch_no": batch_id,
"incoming_rate": valuation,
"qty_after_transaction": qty_after_transaction,
"stock_value_difference": valuation * qty,
"balance_value": balance_value,
"valuation_rate": balance_value / qty_after_transaction,
"actual_qty": qty,
"item_code": batch_item_code,
"warehouse": "_Test Warehouse - _TC",
"stock_queue": json.dumps(stock_queue),
}
)
doc.flags.ignore_permissions = True
doc.flags.ignore_mandatory = True
doc.flags.ignore_links = True
doc.flags.ignore_validate = True
doc.submit()
doc.reload()
frappe.flags.ignore_serial_batch_bundle_validation = False
frappe.flags.use_serial_and_batch_fields = False
batch_doc = frappe.get_doc("Batch", batch_id)
qty = get_batch_qty(batch_id, warehouse, batch_item_code)
self.assertEqual(qty, 30)
sr = create_stock_reconciliation(
item_code=batch_item_code,
posting_date=add_days(nowdate(), -3),
posting_time=nowtime(),
warehouse=warehouse,
qty=100,
rate=1000,
reconcile_all_serial_batch=0,
batch_no=batch_id,
use_serial_batch_fields=1,
)
self.assertEqual(sr.items[0].current_qty, 20)
self.assertEqual(sr.items[0].qty, 100)
qty = get_batch_qty(batch_id, warehouse, batch_item_code)
self.assertEqual(qty, 110)
def create_batch_item_with_batch(item_name, batch_id):
batch_item_doc = create_item(item_name, is_stock_item=1)

View File

@@ -434,7 +434,6 @@
},
{
"default": "1",
"depends_on": "use_serial_batch_fields",
"description": "If enabled, do not update serial / batch values in the stock transactions on creation of auto Serial \n / Batch Bundle. ",
"fieldname": "do_not_update_serial_batch_on_creation_of_auto_bundle",
"fieldtype": "Check",
@@ -460,7 +459,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2024-07-15 17:18:23.872161",
"modified": "2024-07-29 14:55:19.093508",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Settings",

View File

@@ -807,6 +807,9 @@ def get_price_list_rate(args, item_doc, out=None):
if price_list_rate is None or frappe.db.get_single_value(
"Stock Settings", "update_existing_price_list_rate"
):
if args.get("is_internal_supplier") or args.get("is_internal_customer"):
return out
if args.price_list and args.rate:
insert_item_price(args)
@@ -818,7 +821,11 @@ def get_price_list_rate(args, item_doc, out=None):
if frappe.db.get_single_value("Buying Settings", "disable_last_purchase_rate"):
return out
if not out.price_list_rate and args.transaction_type == "buying":
if (
not args.get("is_internal_supplier")
and not out.price_list_rate
and args.transaction_type == "buying"
):
from erpnext.stock.doctype.item.item import get_last_purchase_details
out.update(get_last_purchase_details(item_doc.name, args.name, args.conversion_rate))
@@ -1189,7 +1196,7 @@ def get_batch_qty(batch_no, warehouse, item_code):
@frappe.whitelist()
def apply_price_list(args, as_doc=False):
def apply_price_list(args, as_doc=False, doc=None):
"""Apply pricelist on a document-like dict object and return as
{'parent': dict, 'children': list}
@@ -1228,7 +1235,7 @@ def apply_price_list(args, as_doc=False):
for item in item_list:
args_copy = frappe._dict(args.copy())
args_copy.update(item)
item_details = apply_price_list_on_item(args_copy)
item_details = apply_price_list_on_item(args_copy, doc=doc)
children.append(item_details)
if as_doc:
@@ -1246,10 +1253,10 @@ def apply_price_list(args, as_doc=False):
return {"parent": parent, "children": children}
def apply_price_list_on_item(args):
def apply_price_list_on_item(args, doc=None):
item_doc = frappe.db.get_value("Item", args.item_code, ["name", "variant_of"], as_dict=1)
item_details = get_price_list_rate(args, item_doc)
item_details.update(get_pricing_rule_for_item(args))
item_details.update(get_pricing_rule_for_item(args, doc=doc))
return item_details

View File

@@ -54,6 +54,12 @@ def get_columns(filters):
"width": 150,
"options": "Batch",
},
{
"label": _("Expiry Date"),
"fieldname": "expiry_date",
"fieldtype": "Date",
"width": 120,
},
{"label": _("Balance Qty"), "fieldname": "balance_qty", "fieldtype": "Float", "width": 150},
]
)
@@ -97,6 +103,7 @@ def get_batchwise_data_from_stock_ledger(filters):
table.item_code,
table.batch_no,
table.warehouse,
batch.expiry_date,
Sum(table.actual_qty).as_("balance_qty"),
)
.where(table.is_cancelled == 0)
@@ -127,6 +134,7 @@ def get_batchwise_data_from_serial_batch_bundle(batchwise_data, filters):
table.item_code,
ch_table.batch_no,
table.warehouse,
batch.expiry_date,
Sum(ch_table.qty).as_("balance_qty"),
)
.where((table.is_cancelled == 0) & (table.docstatus == 1))
@@ -152,10 +160,14 @@ def get_query_based_on_filters(query, batch, table, filters):
if filters.batch_no:
query = query.where(batch.name == filters.batch_no)
if not filters.include_expired_batches:
query = query.where((batch.expiry_date >= today()) | (batch.expiry_date.isnull()))
if filters.to_date == today():
query = query.where(batch.batch_qty > 0)
if filters.to_date == today():
if not filters.include_expired_batches:
query = query.where((batch.expiry_date >= today()) | (batch.expiry_date.isnull()))
query = query.where(batch.batch_qty > 0)
else:
query = query.where(table.posting_date <= filters.to_date)
if filters.warehouse:
lft, rgt = frappe.db.get_value("Warehouse", filters.warehouse, ["lft", "rgt"])

View File

@@ -3,6 +3,14 @@
frappe.query_reports["Product Bundle Balance"] = {
filters: [
{
fieldname: "company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
default: frappe.defaults.get_user_default("Company"),
reqd: 1,
},
{
fieldname: "date",
label: __("Date"),

View File

@@ -224,6 +224,9 @@ def get_stock_ledger_entries(filters, items):
.where((sle2.name.isnull()) & (sle.docstatus < 2) & (sle.item_code.isin(items)))
)
if filters.get("company"):
query = query.where(sle.company == filters.get("company"))
if date := filters.get("date"):
query = query.where(sle.posting_date <= date)
else:
@@ -237,7 +240,7 @@ def get_stock_ledger_entries(filters, items):
if warehouse_details:
wh = frappe.qb.DocType("Warehouse")
query = query.where(
ExistsCriterion(
sle.warehouse.isin(
frappe.qb.from_(wh)
.select(wh.name)
.where((wh.lft >= warehouse_details.lft) & (wh.rgt <= warehouse_details.rgt))

View File

@@ -157,6 +157,7 @@ def get_data(filters):
{
"serial_no": bundle_data.get("serial_no"),
"valuation_rate": bundle_data.get("valuation_rate"),
"qty": args.qty,
}
)

View File

@@ -465,10 +465,13 @@ class FIFOSlots:
)
)
for field in ["item_code", "warehouse"]:
for field in ["item_code"]:
if self.filters.get(field):
query = query.where(bundle[field] == self.filters.get(field))
if self.filters.get("warehouse"):
query = self.__get_warehouse_conditions(bundle, query)
bundle_wise_serial_nos = frappe._dict({})
for bundle_name, serial_no in query.run():
bundle_wise_serial_nos.setdefault(bundle_name, []).append(serial_no)

View File

@@ -114,18 +114,23 @@ def validate_filters(filters):
def get_warehouse_list(filters):
from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
if not filters.get("warehouse"):
return frappe.get_all(
"Warehouse",
filters={"company": filters.get("company"), "is_group": 0},
fields=["name"],
order_by="name",
)
wh = frappe.qb.DocType("Warehouse")
query = frappe.qb.from_(wh).select(wh.name).where(wh.is_group == 0)
warehouse = frappe.qb.DocType("Warehouse")
lft, rgt = frappe.db.get_value("Warehouse", filters.get("warehouse"), ["lft", "rgt"])
user_permitted_warehouse = get_permitted_documents("Warehouse")
if user_permitted_warehouse:
query = query.where(wh.name.isin(set(user_permitted_warehouse)))
elif filters.get("warehouse"):
query = query.where(wh.name == filters.get("warehouse"))
return query.run(as_dict=True)
return (
frappe.qb.from_(warehouse)
.select("name")
.where((warehouse.lft >= lft) & (warehouse.rgt <= rgt))
.run(as_dict=True)
)
def add_warehouse_column(columns, warehouse_list):

View File

@@ -4,7 +4,7 @@ import frappe
from frappe import _, bold
from frappe.model.naming import make_autoname
from frappe.query_builder.functions import CombineDatetime, Sum, Timestamp
from frappe.utils import cint, cstr, flt, get_link_to_form, now, nowtime, today
from frappe.utils import add_days, cint, cstr, flt, get_link_to_form, now, nowtime, today
from pypika import Order
from erpnext.stock.deprecated_serial_batch import (
@@ -110,6 +110,7 @@ class SerialBatchBundle:
"type_of_transaction": "Inward" if self.sle.actual_qty > 0 else "Outward",
"company": self.company,
"is_rejected": self.is_rejected_entry(),
"make_bundle_from_sle": 1,
}
).make_serial_and_batch_bundle()
@@ -160,12 +161,13 @@ class SerialBatchBundle:
if msg:
error_msg = (
f"Serial and Batch Bundle not set for item {self.item_code} in warehouse {self.warehouse}."
f"Serial and Batch Bundle not set for item {self.item_code} in warehouse {self.warehouse}"
+ msg
)
frappe.throw(_(error_msg))
def set_serial_and_batch_bundle(self, sn_doc):
self.sle.auto_created_serial_and_batch_bundle = 1
self.sle.db_set({"serial_and_batch_bundle": sn_doc.name, "auto_created_serial_and_batch_bundle": 1})
if sn_doc.is_rejected:
@@ -246,6 +248,9 @@ class SerialBatchBundle:
frappe.throw(_(msg))
def delink_serial_and_batch_bundle(self):
if self.is_pos_transaction():
return
update_values = {
"serial_and_batch_bundle": "",
}
@@ -295,8 +300,22 @@ class SerialBatchBundle:
self.cancel_serial_and_batch_bundle()
def cancel_serial_and_batch_bundle(self):
if self.is_pos_transaction():
return
frappe.get_cached_doc("Serial and Batch Bundle", self.sle.serial_and_batch_bundle).cancel()
def is_pos_transaction(self):
if (
self.sle.voucher_type == "Sales Invoice"
and self.sle.serial_and_batch_bundle
and frappe.get_cached_value(
"Serial and Batch Bundle", self.sle.serial_and_batch_bundle, "voucher_type"
)
== "POS Invoice"
):
return True
def submit_serial_and_batch_bundle(self):
doc = frappe.get_doc("Serial and Batch Bundle", self.sle.serial_and_batch_bundle)
self.validate_actual_qty(doc)
@@ -307,6 +326,9 @@ class SerialBatchBundle:
def set_warehouse_and_status_in_serial_nos(self):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos as get_parsed_serial_nos
if self.sle.auto_created_serial_and_batch_bundle and self.sle.actual_qty > 0:
return
serial_nos = get_serial_nos(self.sle.serial_and_batch_bundle)
if not self.sle.serial_and_batch_bundle and self.sle.serial_no:
serial_nos = get_parsed_serial_nos(self.sle.serial_no)
@@ -321,7 +343,8 @@ class SerialBatchBundle:
status = "Delivered"
sn_table = frappe.qb.DocType("Serial No")
(
query = (
frappe.qb.update(sn_table)
.set(sn_table.warehouse, warehouse)
.set(
@@ -334,7 +357,19 @@ class SerialBatchBundle:
)
.set(sn_table.company, self.sle.company)
.where(sn_table.name.isin(serial_nos))
).run()
)
if status == "Delivered":
warranty_period = frappe.get_cached_value("Item", self.sle.item_code, "warranty_period")
if warranty_period:
warranty_expiry_date = add_days(self.sle.posting_date, cint(warranty_period))
query = query.set(sn_table.warranty_expiry_date, warranty_expiry_date)
query = query.set(sn_table.warranty_period, warranty_period)
else:
query = query.set(sn_table.warranty_expiry_date, None)
query = query.set(sn_table.warranty_period, 0)
query.run()
def set_batch_no_in_serial_nos(self):
entries = frappe.get_all(
@@ -898,6 +933,10 @@ class SerialBatchCreation:
if doc.voucher_no and frappe.get_cached_value(doc.voucher_type, doc.voucher_no, "docstatus") == 2:
doc.voucher_no = ""
doc.flags.ignore_validate_serial_batch = False
if self.get("make_bundle_from_sle") and self.type_of_transaction == "Inward":
doc.flags.ignore_validate_serial_batch = True
doc.save()
self.validate_qty(doc)
@@ -1090,6 +1129,10 @@ class SerialBatchCreation:
msg = f"Please set Serial No Series in the item {self.item_code} or create Serial and Batch Bundle manually."
frappe.throw(_(msg))
voucher_no = ""
if self.get("voucher_no"):
voucher_no = self.get("voucher_no")
for _i in range(abs(cint(self.actual_qty))):
serial_no = make_autoname(self.serial_no_series, "Serial No")
sr_nos.append(serial_no)
@@ -1107,6 +1150,7 @@ class SerialBatchCreation:
self.item_name,
self.description,
"Active",
voucher_no,
self.batch_no,
)
)
@@ -1125,6 +1169,7 @@ class SerialBatchCreation:
"item_name",
"description",
"status",
"purchase_document_no",
"batch_no",
]

View File

@@ -275,7 +275,9 @@ def repost_future_sle(
)
affected_transactions.update(obj.affected_transactions)
distinct_item_warehouses[(args[i].get("item_code"), args[i].get("warehouse"))].reposting_status = True
key = (args[i].get("item_code"), args[i].get("warehouse"))
if distinct_item_warehouses.get(key):
distinct_item_warehouses[key].reposting_status = True
if obj.new_items_found:
for _item_wh, data in distinct_item_warehouses.items():
@@ -1588,9 +1590,11 @@ def get_stock_ledger_entries(
if not previous_sle.get("posting_date"):
previous_sle["posting_datetime"] = "1900-01-01 00:00:00"
else:
previous_sle["posting_datetime"] = get_combine_datetime(
previous_sle["posting_date"], previous_sle["posting_time"]
)
posting_time = previous_sle.get("posting_time")
if not posting_time:
posting_time = "00:00:00"
previous_sle["posting_datetime"] = get_combine_datetime(previous_sle["posting_date"], posting_time)
if operator in (">", "<=") and previous_sle.get("name"):
conditions += " and name!=%(name)s"

View File

@@ -23,18 +23,6 @@
"cost_center",
"dimension_col_break",
"project",
"address_and_contact_section",
"supplier_address",
"address_display",
"contact_person",
"contact_display",
"contact_mobile",
"contact_email",
"column_break_19",
"shipping_address",
"shipping_address_display",
"billing_address",
"billing_address_display",
"section_break_24",
"column_break_25",
"set_warehouse",
@@ -48,10 +36,23 @@
"raw_materials_supplied_section",
"set_reserve_warehouse",
"supplied_items",
"additional_costs_section",
"tab_address_and_contact",
"supplier_address",
"address_display",
"contact_person",
"contact_display",
"contact_mobile",
"contact_email",
"column_break_19",
"shipping_address",
"shipping_address_display",
"billing_address",
"billing_address_display",
"tab_additional_costs",
"distribute_additional_costs_based_on",
"additional_costs",
"total_additional_costs",
"tab_other_info",
"order_status_section",
"status",
"column_break_39",
@@ -59,7 +60,8 @@
"printing_settings_section",
"select_print_heading",
"column_break_43",
"letter_head"
"letter_head",
"tab_connections"
],
"fields": [
{
@@ -95,7 +97,7 @@
"fieldtype": "Link",
"in_global_search": 1,
"in_standard_filter": 1,
"label": "Supplier",
"label": "Job Worker",
"options": "Supplier",
"print_hide": 1,
"reqd": 1,
@@ -107,7 +109,7 @@
"fieldname": "supplier_name",
"fieldtype": "Data",
"in_global_search": 1,
"label": "Supplier Name",
"label": "Job Worker Name",
"read_only": 1,
"reqd": 1
},
@@ -115,7 +117,7 @@
"depends_on": "supplier",
"fieldname": "supplier_warehouse",
"fieldtype": "Link",
"label": "Supplier Warehouse",
"label": "Job Worker Warehouse",
"options": "Warehouse",
"reqd": 1
},
@@ -166,9 +168,8 @@
"read_only": 1
},
{
"collapsible": 1,
"fieldname": "address_and_contact_section",
"fieldtype": "Section Break",
"fieldname": "tab_address_and_contact",
"fieldtype": "Tab Break",
"label": "Address and Contact"
},
{
@@ -176,14 +177,14 @@
"fetch_if_empty": 1,
"fieldname": "supplier_address",
"fieldtype": "Link",
"label": "Supplier Address",
"label": "Job Worker Address",
"options": "Address",
"print_hide": 1
},
{
"fieldname": "address_display",
"fieldtype": "Small Text",
"label": "Supplier Address Details",
"label": "Job Worker Address Details",
"read_only": 1
},
{
@@ -191,7 +192,7 @@
"fetch_if_empty": 1,
"fieldname": "contact_person",
"fieldtype": "Link",
"label": "Supplier Contact",
"label": "Job Worker Contact",
"options": "Contact",
"print_hide": 1
},
@@ -337,11 +338,9 @@
"read_only": 1
},
{
"collapsible": 1,
"collapsible_depends_on": "total_additional_costs",
"depends_on": "eval:(doc.docstatus == 0 || doc.total_additional_costs)",
"fieldname": "additional_costs_section",
"fieldtype": "Section Break",
"fieldname": "tab_additional_costs",
"fieldtype": "Tab Break",
"label": "Additional Costs"
},
{
@@ -449,6 +448,17 @@
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"fieldname": "tab_other_info",
"fieldtype": "Tab Break",
"label": "Other Info"
},
{
"fieldname": "tab_connections",
"fieldtype": "Tab Break",
"label": "Connections",
"show_dashboard": 1
}
],
"icon": "fa fa-file-text",

View File

@@ -23,18 +23,6 @@
"cost_center",
"dimension_col_break",
"project",
"section_addresses",
"supplier_address",
"contact_person",
"address_display",
"contact_display",
"contact_mobile",
"contact_email",
"col_break_address",
"shipping_address",
"shipping_address_display",
"billing_address",
"billing_address_display",
"sec_warehouse",
"set_warehouse",
"rejected_warehouse",
@@ -53,23 +41,36 @@
"get_current_stock",
"raw_material_details",
"supplied_items",
"additional_costs_section",
"distribute_additional_costs_based_on",
"additional_costs",
"total_additional_costs",
"section_break_46",
"in_words",
"bill_no",
"bill_date",
"tab_addresses",
"supplier_address",
"contact_person",
"address_display",
"contact_display",
"contact_mobile",
"contact_email",
"col_break_address",
"shipping_address",
"shipping_address_display",
"billing_address",
"billing_address_display",
"tab_additional_costs",
"distribute_additional_costs_based_on",
"additional_costs",
"total_additional_costs",
"tab_other_info",
"more_info",
"status",
"column_break_39",
"per_returned",
"section_break_47",
"amended_from",
"range",
"column_break4",
"represents_company",
"order_status_section",
"status",
"column_break_39",
"per_returned",
"subscription_detail",
"auto_repeat",
"printing_settings",
@@ -84,7 +85,8 @@
"transporter_name",
"column_break5",
"lr_no",
"lr_date"
"lr_date",
"tab_connections"
],
"fields": [
{
@@ -112,7 +114,7 @@
"fieldname": "supplier",
"fieldtype": "Link",
"in_global_search": 1,
"label": "Supplier",
"label": "Job Worker",
"options": "Supplier",
"print_hide": 1,
"print_width": "150px",
@@ -127,7 +129,7 @@
"fieldname": "supplier_name",
"fieldtype": "Data",
"in_global_search": 1,
"label": "Supplier Name",
"label": "Job Worker Name",
"read_only": 1
},
{
@@ -174,15 +176,14 @@
"width": "150px"
},
{
"collapsible": 1,
"fieldname": "section_addresses",
"fieldtype": "Section Break",
"fieldname": "tab_addresses",
"fieldtype": "Tab Break",
"label": "Address and Contact"
},
{
"fieldname": "supplier_address",
"fieldtype": "Link",
"label": "Select Supplier Address",
"label": "Select Job Worker Address",
"options": "Address",
"print_hide": 1
},
@@ -269,7 +270,7 @@
{
"fieldname": "supplier_warehouse",
"fieldtype": "Link",
"label": "Supplier Warehouse",
"label": "Job Worker Warehouse",
"no_copy": 1,
"options": "Warehouse",
"print_hide": 1,
@@ -414,6 +415,7 @@
"width": "50%"
},
{
"collapsible": 1,
"fieldname": "subscription_detail",
"fieldtype": "Section Break",
"label": "Auto Repeat Detail"
@@ -571,10 +573,6 @@
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "section_break_47",
"fieldtype": "Section Break"
},
{
"collapsible": 1,
"fieldname": "accounting_dimensions_section",
@@ -598,11 +596,9 @@
"options": "Project"
},
{
"collapsible": 1,
"collapsible_depends_on": "total_additional_costs",
"depends_on": "eval:(doc.docstatus == 0 || doc.total_additional_costs)",
"fieldname": "additional_costs_section",
"fieldtype": "Section Break",
"fieldname": "tab_additional_costs",
"fieldtype": "Tab Break",
"label": "Additional Costs"
},
{
@@ -643,7 +639,7 @@
{
"fieldname": "supplier_delivery_note",
"fieldtype": "Data",
"label": "Supplier Delivery Note"
"label": "Job Worker Delivery Note"
},
{
"fieldname": "raw_materials_consumed_section",
@@ -658,6 +654,23 @@
{
"fieldname": "column_break_uinr",
"fieldtype": "Column Break"
},
{
"fieldname": "tab_other_info",
"fieldtype": "Tab Break",
"label": "Other Info"
},
{
"collapsible": 1,
"fieldname": "order_status_section",
"fieldtype": "Section Break",
"label": "Order Status"
},
{
"fieldname": "tab_connections",
"fieldtype": "Tab Break",
"label": "Connections",
"show_dashboard": 1
}
],
"in_create": 1,

File diff suppressed because it is too large Load Diff