Compare commits

...

114 Commits

Author SHA1 Message Date
Frappe PR Bot
c2ae8eaec0 chore(release): Bumped to Version 13.50.4
## [13.50.4](https://github.com/frappe/erpnext/compare/v13.50.3...v13.50.4) (2023-05-16)

### Bug Fixes

* add missing options for `Content Align` ([e37b903](e37b9030fb))
* cancelled vouchers in tax withheld vouchers list ([#35309](https://github.com/frappe/erpnext/issues/35309)) ([188cfc2](188cfc2e3c))
* internal transfer condition ([a1d7170](a1d717053a))
* **Salary Slip:** exchange rate overwritten on form load ([#507](https://github.com/frappe/erpnext/issues/507)) ([#35245](https://github.com/frappe/erpnext/issues/35245)) ([8b3d6ee](8b3d6ee7b0))
* update reference data for statistical component ([77f548c](77f548c814))
2023-05-16 16:59:38 +00:00
Deepesh Garg
f5f88bb62c Merge pull request #35323 from frappe/version-13-hotfix
chore: release v13
2023-05-16 22:28:02 +05:30
mergify[bot]
188cfc2e3c fix: cancelled vouchers in tax withheld vouchers list (#35309)
fix: cancelled vouchers in tax withheld vouchers list (#35309)

(cherry picked from commit 776a83066d)

Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2023-05-16 18:58:23 +05:30
Saurabh
c7c2bad6ab Merge pull request #35267 from saurabh6790/fix-statistical-component-calculation
fix: update reference data for statistical component
2023-05-15 11:09:50 +05:30
Sagar Sharma
4a9ad09c7f Merge pull request #35284 from frappe/mergify/bp/version-13-hotfix/pr-35275
fix: add missing options for `Content Align` (backport #35275)
2023-05-13 09:41:55 +05:30
Sagar Sharma
e37b9030fb fix: add missing options for Content Align
(cherry picked from commit d16caa2d2c)
2023-05-13 04:05:15 +00:00
Saurabh
77f548c814 fix: update reference data for statistical component 2023-05-12 12:10:40 +05:30
Frappe PR Bot
7626d51db1 chore(release): Bumped to Version 13.50.3
## [13.50.3](https://github.com/frappe/erpnext/compare/v13.50.2...v13.50.3) (2023-05-11)

### Bug Fixes

* internal transfer condition ([ac26e4b](ac26e4ba2a))
2023-05-11 17:26:55 +00:00
rohitwaghchaure
48e5846ed5 Merge pull request #35260 from frappe/mergify/bp/version-13/pr-35259
fix: internal transfer condition (backport #35158) (backport #35259)
2023-05-11 22:54:34 +05:30
Rohit Waghchaure
ac26e4ba2a fix: internal transfer condition
(cherry picked from commit b5a2ccf21d)
(cherry picked from commit a1d717053a)
2023-05-11 15:08:08 +00:00
rohitwaghchaure
8b9f8c6ab7 Merge pull request #35259 from frappe/mergify/bp/version-13-hotfix/pr-35158
fix: internal transfer condition (backport #35158)
2023-05-11 20:36:41 +05:30
Rohit Waghchaure
a1d717053a fix: internal transfer condition
(cherry picked from commit b5a2ccf21d)
2023-05-11 14:38:38 +00:00
Rucha Mahabal
8b3d6ee7b0 fix(Salary Slip): exchange rate overwritten on form load (#507) (#35245) 2023-05-10 16:16:50 +05:30
Frappe PR Bot
1380f7a7ec chore(release): Bumped to Version 13.50.2
## [13.50.2](https://github.com/frappe/erpnext/compare/v13.50.1...v13.50.2) (2023-05-10)

### Bug Fixes

* handle empty FBs properly in TB and GL [v14] (backport [#35189](https://github.com/frappe/erpnext/issues/35189)) ([#35192](https://github.com/frappe/erpnext/issues/35192)) ([e2af66c](e2af66c7be))
2023-05-10 05:40:17 +00:00
Deepesh Garg
2825253339 Merge pull request #35223 from frappe/version-13-hotfix
chore: release v13
2023-05-10 11:08:54 +05:30
Frappe PR Bot
40cfd5215c chore(release): Bumped to Version 13.50.1
## [13.50.1](https://github.com/frappe/erpnext/compare/v13.50.0...v13.50.1) (2023-05-06)

### Bug Fixes

* handle empty FBs properly in TB and GL [v14] (backport [#35189](https://github.com/frappe/erpnext/issues/35189)) (backport [#35192](https://github.com/frappe/erpnext/issues/35192)) ([#35195](https://github.com/frappe/erpnext/issues/35195)) ([af8142c](af8142cf85))
2023-05-06 18:13:48 +00:00
mergify[bot]
af8142cf85 fix: handle empty FBs properly in TB and GL [v14] (backport #35189) (backport #35192) (#35195)
fix: handle empty FBs properly in TB and GL [v14] (backport #35189) (#35192)

fix: handle empty FBs properly in TB and GL [v14] (#35189)

fix: handle empty FBs properly in TB and GL
(cherry picked from commit ed5f39c2c2)

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-05-06 23:41:57 +05:30
mergify[bot]
e2af66c7be fix: handle empty FBs properly in TB and GL [v14] (backport #35189) (#35192)
fix: handle empty FBs properly in TB and GL [v14] (#35189)

fix: handle empty FBs properly in TB and GL
(cherry picked from commit ed5f39c2c2)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-05-06 17:44:49 +05:30
Frappe PR Bot
ef2d4febdd chore(release): Bumped to Version 13.50.0
# [13.50.0](https://github.com/frappe/erpnext/compare/v13.49.14...v13.50.0) (2023-05-03)

### Bug Fixes

* allow user to set standard deductions in income tax slab without allowing other exemptions ([c5261cd](c5261cde9c))
* check for session user rather than owner ([7d6e2f9](7d6e2f979f))
* conflicts ([778ba69](778ba6956c))
* conflicts ([b19b0a4](b19b0a4a98))
* conflicts ([6bdf143](6bdf143084))
* don't allow to make reposting for the closed period ([b31d8ee](b31d8eec05))
* handle expected_value_after_useful_life properly in asset value adjustment (backport [#35117](https://github.com/frappe/erpnext/issues/35117)) ([#35120](https://github.com/frappe/erpnext/issues/35120)) ([635559d](635559d905))
* handle finance book properly in trial balance and general ledger ([#35136](https://github.com/frappe/erpnext/issues/35136)) ([9a37603](9a376039aa))
* Hyperlink in Quality Inspection Summary ([54388e8](54388e8d92))
* Naming series error in Journal Entry template ([#35084](https://github.com/frappe/erpnext/issues/35084)) ([d3c769c](d3c769c183))
* per_billed condition for Payment Entry ([#34969](https://github.com/frappe/erpnext/issues/34969)) ([563e5c0](563e5c0b69))
* test case ([db6d0e0](db6d0e03f5))

### Features

* validate repost item valuation against accounts freeze date ([a852dc1](a852dc1f11))
2023-05-03 06:38:15 +00:00
Deepesh Garg
cb0d567d7b Merge pull request #35130 from frappe/version-13-hotfix
chore: release v13
2023-05-03 12:06:38 +05:30
mergify[bot]
9a376039aa fix: handle finance book properly in trial balance and general ledger (#35136)
fix: handle finance book properly in trial balance and general ledger [v14] (#35136)

fix: handle FBs properly in general ledger and trial balance
(cherry picked from commit 344c339484)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-05-03 08:25:01 +05:30
Saurabh
2f74026513 Merge pull request #35114 from saurabh6790/consider-standard-exemption
fix: allow user to set standard deductions in income tax slab without allowing other exemptions
2023-05-02 11:38:30 +05:30
rohitwaghchaure
740313ff09 Merge pull request #35122 from frappe/mergify/bp/version-13-hotfix/pr-35118
fix: don't allow to make reposting for the closed period (backport #35118)
2023-05-02 00:00:58 +05:30
rohitwaghchaure
db6d0e03f5 fix: test case 2023-05-01 23:34:31 +05:30
rohitwaghchaure
778ba6956c fix: conflicts 2023-05-01 20:55:52 +05:30
rohitwaghchaure
b19b0a4a98 fix: conflicts 2023-05-01 20:54:32 +05:30
Rohit Waghchaure
b31d8eec05 fix: don't allow to make reposting for the closed period
(cherry picked from commit f751727149)

# Conflicts:
#	erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
#	erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
2023-05-01 15:12:01 +00:00
rohitwaghchaure
078161cf6b Merge pull request #35116 from frappe/mergify/bp/version-13-hotfix/pr-33013
fix: validate repost item valuation against accounts freeze date (backport #33013)
2023-05-01 20:41:00 +05:30
mergify[bot]
635559d905 fix: handle expected_value_after_useful_life properly in asset value adjustment (backport #35117) (#35120)
fix: handle expected_value_after_useful_life properly in asset value adjustment (#35117)

(cherry picked from commit 80230fec3e)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-05-01 20:00:04 +05:30
rohitwaghchaure
6bdf143084 fix: conflicts 2023-05-01 18:26:51 +05:30
Dany Robert
198a64d574 chore: pre-commit
(cherry picked from commit 88a0aa4077)
2023-05-01 11:50:17 +00:00
Dany Robert
7d6e2f979f fix: check for session user rather than owner
(cherry picked from commit b482e3876d)
2023-05-01 11:50:17 +00:00
Dany Robert
6992e727cf chore: pre-commit
(cherry picked from commit be15419bd5)

# Conflicts:
#	erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
2023-05-01 11:50:16 +00:00
Dany Robert
a852dc1f11 feat: validate repost item valuation against accounts freeze date
(cherry picked from commit 61f05132db)

# Conflicts:
#	erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
2023-05-01 11:50:16 +00:00
Saurabh
c5261cde9c fix: allow user to set standard deductions in income tax slab without allowing other exemptions 2023-05-01 13:33:26 +05:30
mergify[bot]
d3c769c183 fix: Naming series error in Journal Entry template (#35084)
fix: Naming series error in Journal Entry template (#35084)

(cherry picked from commit f3b3dabb9a)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-05-01 13:02:28 +05:30
mergify[bot]
563e5c0b69 fix: per_billed condition for Payment Entry (#34969)
fix: per_billed condition for Payment Entry (#34969)

fix: per_billed condition for Payment Entry (#34969)

(cherry picked from commit d6bc8bba8b)

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-05-01 10:51:23 +05:30
Sagar Sharma
5746ddce84 Merge pull request #35089 from frappe/mergify/bp/version-13-hotfix/pr-35081
fix: Hyperlink in Quality Inspection Summary (backport #35081)
2023-04-28 12:53:51 +05:30
Nihantra Patel
54388e8d92 fix: Hyperlink in Quality Inspection Summary
(cherry picked from commit 72dd7884a8)
2023-04-28 07:19:14 +00:00
Deepesh Garg
784ea7cf48 Merge pull request #35073 from frappe/mergify/bp/version-13/pr-35032
chore: set correct currency symbol in salary register for multi-currency salary slip (backport #35032)
2023-04-27 13:29:08 +05:30
Saurabh
fc42e026ab chore: fix linter
(cherry picked from commit 31bda37970)
2023-04-27 06:10:17 +00:00
Saurabh
cef7126a35 chore: set correct currency symbol in salary register for multi-currency salary slip
(cherry picked from commit 83afaf48df)
2023-04-27 06:10:17 +00:00
Saurabh
297facc1cb Merge pull request #35032 from saurabh6790/salary-register-report-fix
chore: set correct currency symbol in salary register for multi-currency salary slip
2023-04-26 16:36:15 +05:30
Saurabh
31bda37970 chore: fix linter 2023-04-26 15:44:34 +05:30
Frappe PR Bot
1d6917f340 chore(release): Bumped to Version 13.49.14
## [13.49.14](https://github.com/frappe/erpnext/compare/v13.49.13...v13.49.14) (2023-04-25)

### Bug Fixes

* `PermissionError` in Work Order ([5680045](5680045f2b))
* Advance payment against payment terms ([#34872](https://github.com/frappe/erpnext/issues/34872)) ([d215a85](d215a85747))
* internal Purchase Receipt GL Entries ([b73422e](b73422e4ee))
* Payment entry with TDS in bank reco statement ([#34961](https://github.com/frappe/erpnext/issues/34961)) ([5f28b1d](5f28b1d330))
* Payment Request flow fixes from Order to Payment Entry ([#33350](https://github.com/frappe/erpnext/issues/33350)) ([d5a80b5](d5a80b5615))
* SLA permissions ([#34981](https://github.com/frappe/erpnext/issues/34981)) ([c1187be](c1187bed26))
* **test:** `test_backdated_stock_reco_cancellation_future_negative_stock` ([d010b04](d010b048dc))
* Unable to allocate advance against invoice ([#35007](https://github.com/frappe/erpnext/issues/35007)) ([61a3121](61a3121172))
* use filter_by_finance_book instead of only_depreciable_assets in fixed asset register (backport [#35031](https://github.com/frappe/erpnext/issues/35031)) ([#35036](https://github.com/frappe/erpnext/issues/35036)) ([be2095a](be2095ad03))
* validation for internal transfer entry ([91b5a33](91b5a33564))
* value of depreciable assets not updating after manual depr entry [v14] (backport [#35010](https://github.com/frappe/erpnext/issues/35010)) ([#35029](https://github.com/frappe/erpnext/issues/35029)) ([9087ac0](9087ac0829))
* wrong qty of remaining work orders to be created when using "Create" > "Work Order" ([#34726](https://github.com/frappe/erpnext/issues/34726)) ([189b020](189b020d22))
2023-04-25 16:57:14 +00:00
Deepesh Garg
b85d8946f7 Merge pull request #35033 from frappe/version-13-hotfix
chore: release v13
2023-04-25 22:24:49 +05:30
mergify[bot]
5f28b1d330 fix: Payment entry with TDS in bank reco statement (#34961)
fix: Payment entry with TDS in bank reco statement (#34961)

(cherry picked from commit ecea9b44a3)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-04-25 21:13:42 +05:30
mergify[bot]
61a3121172 fix: Unable to allocate advance against invoice (#35007)
fix: Unable to allocate advance against invoice (#35007)

(cherry picked from commit f7b50f2ade)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-04-25 20:51:35 +05:30
mergify[bot]
d5a80b5615 fix: Payment Request flow fixes from Order to Payment Entry (#33350)
* fix: Payment Request flow fixes from Order to Payment Entry

(cherry picked from commit dc178984ae)

# Conflicts:
#	erpnext/buying/doctype/purchase_order/purchase_order.js
#	erpnext/public/js/controllers/transaction.js

* chore: Update test case

(cherry picked from commit e25b98b620)

* chore: More fixes

(cherry picked from commit 31c95deb88)

* chore: resolve conflicts

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-04-25 20:49:18 +05:30
danjeremynavarro
189b020d22 fix: wrong qty of remaining work orders to be created when using "Create" > "Work Order" (#34726)
* fix: convert asynchronous field update to synchronous

* fix: wrong qty of remaining work orders to be created when using "Create" > "Work Order"
2023-04-25 18:53:52 +05:30
mergify[bot]
be2095ad03 fix: use filter_by_finance_book instead of only_depreciable_assets in fixed asset register (backport #35031) (#35036)
fix: use filter_by_finance_book instead of only_depreciable_assets in fixed asset register (#35031)

fix: use filter_by_finance_book instead of only_depreciable_assets
(cherry picked from commit e08d636bf7)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-04-25 15:53:06 +05:30
Saurabh
83afaf48df chore: set correct currency symbol in salary register for multi-currency salary slip 2023-04-25 14:58:37 +05:30
mergify[bot]
9087ac0829 fix: value of depreciable assets not updating after manual depr entry [v14] (backport #35010) (#35029)
* fix: value of depreciable assets not updating after manual depr entry [v14] (#35010)

* fix: update value of asset with calc_depr on after manual depr entry

* fix: value of asset with calc_depr on after manual depr entry not reflecting in asset_depr_and_bal report

* chore: add validation for depr journal entry

* test: manual_depr_for_depreciable_asset and manual_depr_w_incorrect_jv_voucher_type

* chore: unlink depreciable asset from manual depr entry

(cherry picked from commit 3c75e55cb9)

# Conflicts:
#	erpnext/accounts/doctype/journal_entry/journal_entry.js

* chore: fixed conflicts

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-04-25 13:33:51 +05:30
Sagar Sharma
7759d1e390 Merge pull request #34990 from frappe/mergify/bp/version-13-hotfix/pr-34981
fix: SLA permissions (backport #34981)
2023-04-22 10:31:41 +05:30
s-aga-r
cfa1a2b050 chore: conflicts 2023-04-22 10:08:26 +05:30
Ankush Menat
c1187bed26 fix: SLA permissions (#34981)
(cherry picked from commit ac871797b2)

# Conflicts:
#	erpnext/support/doctype/service_level_agreement/service_level_agreement.json
2023-04-21 15:44:45 +00:00
rohitwaghchaure
9957981039 Merge pull request #34984 from frappe/mergify/bp/version-13-hotfix/pr-34980
fix: validation for internal transfer entry (backport #34980)
2023-04-21 18:53:28 +05:30
rohitwaghchaure
82b46f2bfe chore: fix conflicts 2023-04-21 17:52:11 +05:30
Rohit Waghchaure
91b5a33564 fix: validation for internal transfer entry
(cherry picked from commit 19911b48fd)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.js
2023-04-21 12:16:43 +00:00
mergify[bot]
d215a85747 fix: Advance payment against payment terms (#34872)
* fix: Advance payment against payment terms (#34872)

(cherry picked from commit 5c75894065)

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

* chore: resolve conflicts

* chore: fix tests

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-04-21 13:24:07 +05:30
Sagar Sharma
813b4d4de2 Merge pull request #34945 from frappe/mergify/bp/version-13-hotfix/pr-34912
fix: internal Purchase Receipt GL Entries (backport #34912)
2023-04-20 17:13:06 +05:30
Sagar Sharma
983140acd8 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-34912 2023-04-20 16:21:11 +05:30
s-aga-r
102ac9f74d chore: conflicts 2023-04-20 16:20:25 +05:30
Sagar Sharma
ebf8deb933 Merge pull request #34957 from frappe/mergify/bp/version-13-hotfix/pr-34953
fix: `PermissionError` in Work Order (backport #34953)
2023-04-20 16:01:36 +05:30
s-aga-r
5680045f2b fix: PermissionError in Work Order
(cherry picked from commit 8108b2de0a)
2023-04-20 10:28:04 +00:00
s-aga-r
d010b048dc fix(test): test_backdated_stock_reco_cancellation_future_negative_stock
(cherry picked from commit 11c8503180)
2023-04-20 06:06:05 +00:00
s-aga-r
046bf64fa3 test: add test case for internal PR GL Entries
(cherry picked from commit c86c543fbf)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
2023-04-20 06:06:05 +00:00
s-aga-r
b73422e4ee fix: internal Purchase Receipt GL Entries
(cherry picked from commit 6fca9adcd4)
2023-04-20 06:06:04 +00:00
Frappe PR Bot
acecd07fa2 chore(release): Bumped to Version 13.49.13
## [13.49.13](https://github.com/frappe/erpnext/compare/v13.49.12...v13.49.13) (2023-04-19)

### Bug Fixes

* change discuss forum url ([#34891](https://github.com/frappe/erpnext/issues/34891)) ([ba984ac](ba984acef2))
* unable to change `company` for manual `Serial No` entry ([5d51103](5d511035ec))
2023-04-19 01:36:18 +00:00
Deepesh Garg
5c6134f1b0 Merge pull request #34906 from frappe/version-13-hotfix
chore: release v13
2023-04-19 07:04:53 +05:30
Sagar Sharma
a2d2beb610 Merge pull request #34869 from frappe/mergify/bp/version-13-hotfix/pr-34858
fix: unable to change `company` for manual `Serial No` entry (backport #34858)
2023-04-18 21:29:02 +05:30
Sagar Sharma
3f4c322bef Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-34858 2023-04-18 11:38:11 +05:30
mergify[bot]
ba984acef2 fix: change discuss forum url (#34891)
fix: change discuss forum url (#34891)

[skip ci]

(cherry picked from commit dd93ea067e)

Co-authored-by: MohsinAli <mmatiyailol@gmail.com>
2023-04-18 07:42:18 +05:30
s-aga-r
98ed6445a8 chore: conflicts 2023-04-16 20:10:18 +05:30
s-aga-r
5d511035ec fix: unable to change company for manual Serial No entry
(cherry picked from commit fb3271c624)

# Conflicts:
#	erpnext/stock/doctype/serial_no/serial_no.json
2023-04-15 06:41:00 +00:00
mergify[bot]
7655a4f0d1 chore: update CODEOWNERS (#34817)
* chore: update CODEOWNERS

[skip ci]

(cherry picked from commit aa8b241d5a)

# Conflicts:
#	CODEOWNERS

* fix: conflicts

* chore: remove duplicates

* chore: remove duplicates

---------

Co-authored-by: Saqib Ansari <nextchamp.saqib@gmail.com>
2023-04-12 07:38:35 +05:30
Frappe PR Bot
c93a5ab8f0 chore(release): Bumped to Version 13.49.12
## [13.49.12](https://github.com/frappe/erpnext/compare/v13.49.11...v13.49.12) (2023-04-11)

### Bug Fixes

* `payment entry is already created` on posawesome. (backport [#34712](https://github.com/frappe/erpnext/issues/34712)) ([#34753](https://github.com/frappe/erpnext/issues/34753)) ([b48fca3](b48fca3e5a))
* Allocate tax loss to tax account head on early payment discount ([#34287](https://github.com/frappe/erpnext/issues/34287)) ([92a26dd](92a26dda3c))
* asset monthly WDV and DD schedule [v13] ([#34645](https://github.com/frappe/erpnext/issues/34645)) ([fed43ae](fed43aeb85))
* BOM Update Cost, when no actual qty ([9725698](9725698b79))
* bom update log not working for large batch size ([9cf30d7](9cf30d7621))
* don't include cancelled JVs in assdeprledger report ([#34737](https://github.com/frappe/erpnext/issues/34737)) ([3007ac3](3007ac3c20))
* enclose ternary operator in parentheses ([198830a](198830a6c8))
* filter out old allocation's cf leaves while fetching leave details ([#34723](https://github.com/frappe/erpnext/issues/34723)) ([50de045](50de045247))
* format currency/float as per number format in work history ([#34545](https://github.com/frappe/erpnext/issues/34545)) ([892c480](892c480408))
* incorrect arg name in asset value adjustment ([545807a](545807a91e))
* incorrect balance qty in the stock ledger report ([dab1f1a](dab1f1a0d0))
* Item tax validity comparison fixes ([#34784](https://github.com/frappe/erpnext/issues/34784)) ([71bafab](71bafab41b))
* lost opportunity report issue ([#34626](https://github.com/frappe/erpnext/issues/34626)) ([ab06cb4](ab06cb42a3))
* posting time issue ([f22e777](f22e7775b3))
* provide filter by depreciable assets in fixed asset register ([#34803](https://github.com/frappe/erpnext/issues/34803)) ([8609bf4](8609bf4a12))
* serial no with zero quantity issue in stock reco ([46638b1](46638b19db))
* Shop by category fixes (backport [#34688](https://github.com/frappe/erpnext/issues/34688)) ([#34751](https://github.com/frappe/erpnext/issues/34751)) ([af828e4](af828e4554))

### Reverts

* Revert "fix: `payment entry is already created` on posawesome. (#34712)" ([034e35e](034e35e7f6)), closes [#34712](https://github.com/frappe/erpnext/issues/34712) [#34712](https://github.com/frappe/erpnext/issues/34712) [#34753](https://github.com/frappe/erpnext/issues/34753)
* remove frappe.send_message (v13) ([#34820](https://github.com/frappe/erpnext/issues/34820)) ([77f1322](77f1322732)), closes [#34816](https://github.com/frappe/erpnext/issues/34816)
2023-04-11 11:41:15 +00:00
Deepesh Garg
12cbe38299 Merge pull request #34812 from frappe/version-13-hotfix
chore: release v13
2023-04-11 17:09:06 +05:30
mergify[bot]
77f1322732 revert: remove frappe.send_message (v13) (#34820)
revert: remove frappe.send_message (v14) (#34816)

revert: remove frappe.send_message
(cherry picked from commit 8a331e0f26)

Co-authored-by: Ritwik Puri <ritwikpuri5678@gmail.com>
2023-04-11 16:22:51 +05:30
mergify[bot]
8609bf4a12 fix: provide filter by depreciable assets in fixed asset register (#34803)
fix: provide filter by depreciable assets in fixed asset register (#34803)

(cherry picked from commit c957a5cd2e)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-04-11 13:50:23 +05:30
mergify[bot]
71bafab41b fix: Item tax validity comparison fixes (#34784)
fix: Item tax validity comparison fixes (#34784)

fix: Item tax validity comparsion fixes
(cherry picked from commit 6f6928fa7b)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-04-10 12:39:44 +05:30
Anand Baburajan
ce151ddae4 Merge pull request #34780 from frappe/mergify/bp/version-13-hotfix/pr-34735
'Make Asset Movement' button translation fix in asset_list.js (backport #34735)
2023-04-07 15:34:32 +05:30
Hossein Yousefian
16ae117c97 'Make Asset Movement' button translation fix
(cherry picked from commit b70615ef18)
2023-04-07 10:01:19 +00:00
Deepesh Garg
034e35e7f6 Revert "fix: payment entry is already created on posawesome. (#34712)"
Revert "fix: `payment entry is already created` on posawesome. (backport #34712) (#34753)"

This reverts commit b48fca3e5a.
2023-04-05 17:12:05 +05:30
mergify[bot]
b48fca3e5a fix: payment entry is already created on posawesome. (backport #34712) (#34753) 2023-04-05 13:55:33 +05:30
mergify[bot]
af828e4554 fix: Shop by category fixes (backport #34688) (#34751)
fix: Shop by category fixes (#34688)

* fix: Shop by category fixes

* chore: Update tests

(cherry picked from commit 56f5078357)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-04-05 13:09:49 +05:30
mergify[bot]
e76df6ff46 fix!: require sender and message for contact us page (#34707)
fix!: require sender and message for contact us page (#34707)

* fix: require sender and message for contact us page

* refactor: dont override frappe.send_message from client side

used override_whitelisted_method hook for the same

(cherry picked from commit f193393f57)

Co-authored-by: Ritwik Puri <ritwikpuri5678@gmail.com>
2023-04-05 12:46:56 +05:30
mergify[bot]
3007ac3c20 fix: don't include cancelled JVs in assdeprledger report (#34737)
fix: don't include cancelled JVs in assdeprledger report

(cherry picked from commit 3896d41e95)

Co-authored-by: anandbaburajan <anandbaburajan@gmail.com>
2023-04-05 11:56:38 +05:30
mergify[bot]
92a26dda3c fix: Allocate tax loss to tax account head on early payment discount (#34287)
* fix: Taxes aren't discounted on early payment discount

- Deductions in payment entry must be split into income loss and tax loss
- Compute total discount in percentage, makes discounting different amounts proportionately easier

(cherry picked from commit 768c3a4927)

* fix: Recalculate difference amount after setting deductions

(cherry picked from commit 75ec0a0a85)

* fix: Set deductions in base currency

- Use field precision to get more accurate values

(cherry picked from commit dc2998f544)

* fix: Back update discounted amount in Invoice based on discount type

- Discount value was always trated as a percentage on back updation

(cherry picked from commit 2ae5834290)

* test: PE from SI with early payment discount amount & PE assertions in discount % test

(cherry picked from commit c217bb2018)

* fix: Set deduction amount in company currency on Doctype

- Even via JS, deductions amount is always in company currency
- Since there is nothing dynamic about this field, set it in the doctype spec itself
- fixed: Inconsistency between label currency and field currency formatted value

(cherry picked from commit 7f2e7badff)

* fix: Don't add to deductions if amount is 0

- misc: better docstring

(cherry picked from commit f02fc8acf0)

* fix: Paid amount must be discounted considering accounting currency

- Accounting is in the same currency if party currency and company currency is the same
- If accounting is in the same currency, paid and recvd amount is in the base currency
- Then, discount amount must also be in the base currency as it is deducted from paid amount
- Received amount must be in base currency if not multi currency
- cleanup: Deductions setting broken into smaller functions

(cherry picked from commit 761f68d7bf)

* fix: Multi-currency SI with base currency PE

- Return total discount loss in base currency
- Allocate payment based on terms: Set allocated amount in references table in base currency if accounting is in that currency
- Allocate payment based on terms: While back updating set paid amount (payment schedule) in transaction currency always
- minor: discount msgprint in correct currency

(cherry picked from commit b09c2381ca)

* test: Multi currency SI with multi-currency accounting and single currency accounting + Early payment discount

(cherry picked from commit 9abf0ef615)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/test_payment_entry.py

* fix: Handle rounding more gracefully

- Round off pending discount loss to avoid miniscule losses rounded to 0.0 that are added in deductions
- Use base amounts to calculate base losses instead of using conversion factor which increases rounding error
- Round of total base loss instead of individual income and tax losses to reduce rounding error
- Use default round off account for pending rounding loss in deductions

(cherry picked from commit caa1a3dccf)

* fix: Provision to apply early payment discount if payment is recorded late

- Party could have paid on time but payment is recorded late
- Prompt for reference date so that discount is applied while mapping
- Prompt only if discount in payment schedule of valid doctypes
- test: Reference date and impact on PE
- `make_payment_entry` (JS) must be able to access `this`

(cherry picked from commit d6d0163514)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/payment_entry.py
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
#	erpnext/buying/doctype/purchase_order/purchase_order.js
#	erpnext/public/js/controllers/transaction.js

* feat: Make Tax loss booking optional

- Checkbox in Accounts Settings
- Apply checkbox in PE deductions setting logic
- Adjust tests

(cherry picked from commit 216a46bd66)

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

* fix: Merge conflicts

* fix: 'Donation' does not have `company_currency` field

- Make sure check uses this field only for eligible documents

---------

Co-authored-by: marination <maricadsouza221197@gmail.com>
2023-04-05 11:55:22 +05:30
Anand Baburajan
fed43aeb85 fix: asset monthly WDV and DD schedule [v13] (#34645)
fix: monthly wdv and dd schedule
2023-04-05 11:45:15 +05:30
RJPvT
892c480408 fix: format currency/float as per number format in work history (#34545)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2023-04-04 11:08:55 +05:30
Rucha Mahabal
50de045247 fix: filter out old allocation's cf leaves while fetching leave details (#34723) 2023-04-03 20:19:11 +05:30
rohitwaghchaure
313aecf0ff Merge pull request #34720 from frappe/mergify/bp/version-13-hotfix/pr-34715
fix: bom update log not working for large batch size (backport #34715)
2023-04-03 16:55:37 +05:30
Rohit Waghchaure
9cf30d7621 fix: bom update log not working for large batch size
(cherry picked from commit d56070301c)
2023-04-03 10:20:07 +00:00
Sagar Sharma
e7fd47ae82 Merge pull request #34698 from frappe/mergify/bp/version-13-hotfix/pr-34656
fix: BOM Update Cost, when no actual qty (backport #34656)
2023-04-02 14:59:31 +05:30
s-aga-r
9725698b79 fix: BOM Update Cost, when no actual qty
(cherry picked from commit a4112c75c5)
2023-04-01 10:54:51 +00:00
ruthra kumar
008c985392 Merge pull request #34685 from frappe/mergify/bp/version-13-hotfix/pr-34679
fix: enclose ternary operator in parenthesis (backport #34679)
2023-03-31 14:05:16 +05:30
ruthra kumar
198830a6c8 fix: enclose ternary operator in parentheses
(cherry picked from commit 986daa6578)
2023-03-31 08:02:11 +00:00
Sagar Sharma
2770840946 Merge pull request #34682 from frappe/mergify/bp/version-13-hotfix/pr-34677
chore: make `Production Plan Item Reference` table hidden in Production Plan (backport #34677)
2023-03-31 12:43:26 +05:30
s-aga-r
45eb440b68 chore: conflicts 2023-03-31 12:42:51 +05:30
s-aga-r
85d8ed989d chore: make Production Plan Item Reference table hidden in Production Plan
(cherry picked from commit 706be2a415)

# Conflicts:
#	erpnext/manufacturing/doctype/production_plan/production_plan.json
2023-03-31 07:05:19 +00:00
rohitwaghchaure
6b026280c7 Merge pull request #34670 from frappe/mergify/bp/version-13-hotfix/pr-34664
fix: incorrect balance qty in the stock ledger report (backport #34664)
2023-03-30 18:17:51 +05:30
rohitwaghchaure
74280e0557 Merge pull request #34672 from frappe/mergify/bp/version-13-hotfix/pr-34636
fix: posting time issue (backport #34636)
2023-03-30 18:17:14 +05:30
Anand Baburajan
5f32696158 Merge pull request #34666 from frappe/mergify/bp/version-13-hotfix/pr-34661
chore: improve asset depr posting failure msg (backport #34661)
2023-03-30 17:50:32 +05:30
Rohit Waghchaure
f22e7775b3 fix: posting time issue
(cherry picked from commit 345e6facbe)
2023-03-30 11:56:41 +00:00
Rohit Waghchaure
dab1f1a0d0 fix: incorrect balance qty in the stock ledger report
(cherry picked from commit cbdaab940d)
2023-03-30 11:56:28 +00:00
Anand Baburajan
503c58edf8 chore: improve asset depr posting failure msg (#34661)
* chore: improve asset depr posting error msg

* chore: add period

* chore: improve msg

(cherry picked from commit d999dea3e4)
2023-03-30 11:09:34 +00:00
mergify[bot]
e54ff346ca chore: auto fill asset name and available for use date (backport #34660) (#34663)
* chore: auto fill asset name and available for use date

(cherry picked from commit af3e807607)

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

* Update asset.json

---------

Co-authored-by: anandbaburajan <anandbaburajan@gmail.com>
2023-03-30 16:36:56 +05:30
rohitwaghchaure
a872a7a9eb Merge pull request #34653 from frappe/mergify/bp/version-13-hotfix/pr-34648
fix: serial no with zero quantity issue in stock reco (backport #34648)
2023-03-30 13:39:28 +05:30
Anand Baburajan
60046feac3 Merge pull request #34651 from frappe/mergify/bp/version-13-hotfix/pr-34649
fix: incorrect arg name in asset value adjustment (backport #34649)
2023-03-30 13:10:07 +05:30
Rohit Waghchaure
46638b19db fix: serial no with zero quantity issue in stock reco
(cherry picked from commit 17131e5a02)
2023-03-30 07:35:38 +00:00
anandbaburajan
545807a91e fix: incorrect arg name in asset value adjustment
(cherry picked from commit 2b0470d1f5)
2023-03-30 07:34:09 +00:00
mergify[bot]
ab06cb42a3 fix: lost opportunity report issue (#34626)
fix: lost opportunity report issue (#34626)

* fix: lost opportunity report issue

* chore: Linting Issues

---------

Co-authored-by: Komal Saraf <komal@frappe.io>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
(cherry picked from commit d0660ad222)

Co-authored-by: Komal-Saraf0609 <81952590+Komal-Saraf0609@users.noreply.github.com>
2023-03-29 17:04:40 +05:30
Bevan Tony Medrano
625b8e8005 Asset maintenance task add dropdown "3 Yearly" (#34607)
* feat(asset_maintenance.json):Add 3 yearly in periodicity dropdown

* add server side implications for 3 yearly
2023-03-29 16:59:49 +05:30
48 changed files with 883 additions and 396 deletions

View File

@@ -3,14 +3,13 @@
# These owners will be the default owners for everything in
# the repo. Unless a later match takes precedence,
erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/accounts/ @deepeshgarg007 @ruthra-kumar
erpnext/assets/ @anandbaburajan @deepeshgarg007
erpnext/erpnext_integrations/ @nextchamp-saqib
erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007
erpnext/regional @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/selling @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/support/ @nextchamp-saqib @deepeshgarg007
pos* @nextchamp-saqib
erpnext/loan_management/ @deepeshgarg007
erpnext/regional @deepeshgarg007 @ruthra-kumar
erpnext/selling @deepeshgarg007 @ruthra-kumar
erpnext/support/ @deepeshgarg007
pos*
erpnext/buying/ @rohitwaghchaure @s-aga-r
erpnext/maintenance/ @rohitwaghchaure @s-aga-r
@@ -18,16 +17,10 @@ erpnext/manufacturing/ @rohitwaghchaure @s-aga-r
erpnext/quality_management/ @rohitwaghchaure @s-aga-r
erpnext/stock/ @rohitwaghchaure @s-aga-r
erpnext/controllers @deepeshgarg007 @rohitwaghchaure
erpnext/patches/ @deepeshgarg007 @rohitwaghchaure
requirements.txt @ankush
erpnext/healthcare/ @chillaranand
erpnext/hr/ @ruchamahabal
erpnext/non_profit/ @ruchamahabal
erpnext/payroll @ruchamahabal
erpnext/projects/ @ruchamahabal
.github/ @deepeshgarg007
pyproject.toml @ankush
erpnext/controllers @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination
erpnext/patches/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure
erpnext/public/ @nextchamp-saqib @marination
.github/ @ankush
requirements.txt @gavindsouza @ankush

View File

@@ -4,7 +4,7 @@ import frappe
from erpnext.hooks import regional_overrides
__version__ = "13.49.11"
__version__ = "13.50.4"
def get_default_company(user=None):

View File

@@ -297,7 +297,7 @@ def _make_test_records(verbose=None):
# fixed asset depreciation
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None],
["_Test Depreciations", "Expenses", 0, None, None],
["_Test Depreciations", "Expenses", 0, "Depreciation", None],
["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
# Receivable / Payable Account
["_Test Receivable", "Current Assets", 0, "Receivable", None],

View File

@@ -175,6 +175,7 @@
},
{
"default": "0",
"description": "Payment Terms from orders will be fetched into the invoices as is",
"fieldname": "automatically_fetch_payment_terms",
"fieldtype": "Check",
"label": "Automatically Fetch Payment Terms from Order"
@@ -299,7 +300,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-03-28 09:50:20.375233",
"modified": "2023-04-14 17:22:03.680886",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -56,7 +56,7 @@ class BankClearance(Document):
select
"Payment Entry" as payment_document, name as payment_entry,
reference_no as cheque_number, reference_date as cheque_date,
if(paid_from=%(account)s, paid_amount, 0) as credit,
if(paid_from=%(account)s, paid_amount + total_taxes_and_charges, 0) as credit,
if(paid_from=%(account)s, 0, received_amount) as debit,
posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency

View File

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

View File

@@ -75,6 +75,7 @@ class JournalEntry(AccountsController):
self.validate_empty_accounts_table()
self.set_account_and_party_balance()
self.validate_inter_company_accounts()
self.validate_depr_entry_voucher_type()
if self.docstatus == 0:
self.apply_tax_withholding()
@@ -134,6 +135,13 @@ class JournalEntry(AccountsController):
if self.total_credit != doc.total_debit or self.total_debit != doc.total_credit:
frappe.throw(_("Total Credit/ Debit Amount should be same as linked Journal Entry"))
def validate_depr_entry_voucher_type(self):
if (
any(d.account_type == "Depreciation" for d in self.get("accounts"))
and self.voucher_type != "Depreciation Entry"
):
frappe.throw(_("Journal Entry type should be set as Depreciation Entry for asset depreciation"))
def validate_stock_accounts(self):
stock_accounts = get_stock_accounts(self.company, self.doctype, self.name)
for account in stock_accounts:
@@ -237,25 +245,30 @@ class JournalEntry(AccountsController):
self.remove(d)
def update_asset_value(self):
if self.voucher_type != "Depreciation Entry":
if self.flags.planned_depr_entry or self.voucher_type != "Depreciation Entry":
return
processed_assets = []
for d in self.get("accounts"):
if (
d.reference_type == "Asset" and d.reference_name and d.reference_name not in processed_assets
d.reference_type == "Asset"
and d.reference_name
and d.account_type == "Depreciation"
and d.debit
):
processed_assets.append(d.reference_name)
asset = frappe.get_doc("Asset", d.reference_name)
if asset.calculate_depreciation:
continue
depr_value = d.debit or d.credit
asset.db_set("value_after_depreciation", asset.value_after_depreciation - depr_value)
fb_idx = 1
if self.finance_book:
for fb_row in asset.get("finance_books"):
if fb_row.finance_book == self.finance_book:
fb_idx = fb_row.idx
break
fb_row = asset.get("finance_books")[fb_idx - 1]
fb_row.value_after_depreciation -= d.debit
fb_row.db_update()
else:
asset.db_set("value_after_depreciation", asset.value_after_depreciation - d.debit)
asset.set_status()
@@ -320,35 +333,35 @@ class JournalEntry(AccountsController):
if self.voucher_type != "Depreciation Entry":
return
processed_assets = []
for d in self.get("accounts"):
if (
d.reference_type == "Asset" and d.reference_name and d.reference_name not in processed_assets
d.reference_type == "Asset"
and d.reference_name
and d.account_type == "Depreciation"
and d.debit
):
processed_assets.append(d.reference_name)
asset = frappe.get_doc("Asset", d.reference_name)
if asset.calculate_depreciation:
fb_idx = None
for s in asset.get("schedules"):
if s.journal_entry == self.name:
s.db_set("journal_entry", None)
idx = cint(s.finance_book_id) or 1
finance_books = asset.get("finance_books")[idx - 1]
finance_books.value_after_depreciation += s.depreciation_amount
finance_books.db_update()
asset.set_status()
fb_idx = cint(s.finance_book_id) or 1
break
if not fb_idx:
fb_idx = 1
if self.finance_book:
for fb_row in asset.get("finance_books"):
if fb_row.finance_book == self.finance_book:
fb_idx = fb_row.idx
break
fb_row = asset.get("finance_books")[fb_idx - 1]
fb_row.value_after_depreciation += d.debit
fb_row.db_update()
else:
depr_value = d.debit or d.credit
asset.db_set("value_after_depreciation", asset.value_after_depreciation + depr_value)
asset.set_status()
asset.db_set("value_after_depreciation", asset.value_after_depreciation + d.debit)
asset.set_status()
def unlink_inter_company_jv(self):
if (

View File

@@ -2,6 +2,21 @@
// For license information, please see license.txt
frappe.ui.form.on("Journal Entry Template", {
onload: function(frm) {
if(frm.is_new()) {
frappe.call({
type: "GET",
method: "erpnext.accounts.doctype.journal_entry_template.journal_entry_template.get_naming_series",
callback: function(r){
if(r.message) {
frm.set_df_property("naming_series", "options", r.message.split("\n"));
frm.set_value("naming_series", r.message.split("\n")[0]);
frm.refresh_field("naming_series");
}
}
});
}
},
refresh: function(frm) {
frappe.model.set_default_values(frm.doc);
@@ -19,18 +34,6 @@ frappe.ui.form.on("Journal Entry Template", {
return { filters: filters };
});
frappe.call({
type: "GET",
method: "erpnext.accounts.doctype.journal_entry_template.journal_entry_template.get_naming_series",
callback: function(r){
if(r.message){
frm.set_df_property("naming_series", "options", r.message.split("\n"));
frm.set_value("naming_series", r.message.split("\n")[0]);
frm.refresh_field("naming_series");
}
}
});
},
voucher_type: function(frm) {
var add_accounts = function(doc, r) {

View File

@@ -1855,7 +1855,10 @@ def get_payment_entry(
):
reference_doc = None
doc = frappe.get_doc(dt, dn)
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0:
over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= (
100.0 + over_billing_allowance
):
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
party_type = set_party_type(dt)
@@ -1917,7 +1920,12 @@ def get_payment_entry(
if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked():
frappe.msgprint(_("{0} is on hold till {1}").format(doc.name, doc.release_date))
else:
if doc.doctype in ("Sales Invoice", "Purchase Invoice") and frappe.get_value(
if doc.doctype in (
"Sales Invoice",
"Purchase Invoice",
"Purchase Order",
"Sales Order",
) and frappe.get_value(
"Payment Terms Template",
{"name": doc.payment_terms_template},
"allocate_payment_based_on_payment_terms",

View File

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

View File

@@ -254,6 +254,7 @@ class PaymentRequest(Document):
payment_entry.update(
{
"mode_of_payment": self.mode_of_payment,
"reference_no": self.name,
"reference_date": nowdate(),
"remarks": "Payment Entry against {0} {1} via Payment Request {2}".format(
@@ -403,25 +404,22 @@ def make_payment_request(**args):
else ""
)
existing_payment_request = None
if args.order_type == "Shopping Cart":
existing_payment_request = frappe.db.get_value(
"Payment Request",
{"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": ("!=", 2)},
)
draft_payment_request = frappe.db.get_value(
"Payment Request",
{"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": 0},
)
if existing_payment_request:
existing_payment_request_amount = get_existing_payment_request_amount(args.dt, args.dn)
if existing_payment_request_amount:
grand_total -= existing_payment_request_amount
if draft_payment_request:
frappe.db.set_value(
"Payment Request", existing_payment_request, "grand_total", grand_total, update_modified=False
"Payment Request", draft_payment_request, "grand_total", grand_total, update_modified=False
)
pr = frappe.get_doc("Payment Request", existing_payment_request)
pr = frappe.get_doc("Payment Request", draft_payment_request)
else:
if args.order_type != "Shopping Cart":
existing_payment_request_amount = get_existing_payment_request_amount(args.dt, args.dn)
if existing_payment_request_amount:
grand_total -= existing_payment_request_amount
pr = frappe.new_doc("Payment Request")
pr.update(
{

View File

@@ -303,7 +303,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
apply_tds: function(frm) {
var me = this;
me.frm.set_value("tax_withheld_vouchers", []);
if (!me.frm.doc.apply_tds) {
me.frm.set_value("tax_withholding_category", '');
me.frm.set_df_property("tax_withholding_category", "hidden", 1);

View File

@@ -316,6 +316,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
},
make_inter_company_invoice: function() {
let me = this;
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_inter_company_purchase_invoice",
frm: me.frm

View File

@@ -114,28 +114,6 @@ def get_assets(filters):
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
from (SELECT a.asset_category,
ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
ds.depreciation_amount
else
0
end), 0) as accumulated_depreciation_as_on_from_date,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s and ds.schedule_date <= a.disposal_date then
ds.depreciation_amount
else
0
end), 0) as depreciation_eliminated_during_the_period,
ifnull(sum(case when ds.schedule_date >= %(from_date)s and ds.schedule_date <= %(to_date)s
and (ifnull(a.disposal_date, 0) = 0 or ds.schedule_date <= a.disposal_date) then
ds.depreciation_amount
else
0
end), 0) as depreciation_amount_during_the_period
from `tabAsset` a, `tabDepreciation Schedule` ds
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != ''
group by a.asset_category
union
SELECT a.asset_category,
ifnull(sum(case when gle.posting_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
gle.debit
else
@@ -160,7 +138,7 @@ def get_assets(filters):
aca.parent = a.asset_category and aca.company_name = %(company)s
join `tabCompany` company on
company.name = %(company)s
where a.docstatus=1 and a.company=%(company)s and a.calculate_depreciation=0 and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
group by a.asset_category
union
SELECT a.asset_category,

View File

@@ -79,7 +79,7 @@ def get_entries(filters):
payment_entries = frappe.db.sql(
"""SELECT
"Payment Entry", name, posting_date, reference_no, clearance_date, party,
if(paid_from=%(account)s, paid_amount * -1, received_amount)
if(paid_from=%(account)s, ((paid_amount * -1) - total_taxes_and_charges) , received_amount)
FROM
`tabPayment Entry`
WHERE

View File

@@ -524,11 +524,26 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters):
additional_conditions.append("cost_center in %(cost_center)s")
if filters.get("include_default_book_entries"):
additional_conditions.append(
"(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)"
)
if filters.get("finance_book"):
if filters.get("company_fb") and cstr(filters.get("finance_book")) != cstr(
filters.get("company_fb")
):
frappe.throw(
_("To use a different finance book, please uncheck 'Include Default Book Entries'")
)
else:
additional_conditions.append(
"(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)"
)
else:
additional_conditions.append("(finance_book in (%(company_fb)s, '') OR finance_book IS NULL)")
else:
additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)")
if filters.get("finance_book"):
additional_conditions.append(
"(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)"
)
else:
additional_conditions.append("(finance_book in ('') OR finance_book IS NULL)")
if accounting_dimensions:
for dimension in accounting_dimensions:

View File

@@ -176,7 +176,8 @@ frappe.query_reports["General Ledger"] = {
{
"fieldname": "include_default_book_entries",
"label": __("Include Default Book Entries"),
"fieldtype": "Check"
"fieldtype": "Check",
"default": 1
},
{
"fieldname": "show_cancelled_entries",

View File

@@ -287,13 +287,23 @@ def get_conditions(filters):
if filters.get("project"):
conditions.append("project in %(project)s")
if filters.get("finance_book"):
if filters.get("include_default_book_entries"):
conditions.append(
"(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)"
)
if filters.get("include_default_book_entries"):
if filters.get("finance_book"):
if filters.get("company_fb") and cstr(filters.get("finance_book")) != cstr(
filters.get("company_fb")
):
frappe.throw(
_("To use a different finance book, please uncheck 'Include Default Book Entries'")
)
else:
conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)")
else:
conditions.append("finance_book in (%(finance_book)s)")
conditions.append("(finance_book in (%(company_fb)s, '') OR finance_book IS NULL)")
else:
if filters.get("finance_book"):
conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)")
else:
conditions.append("(finance_book in ('') OR finance_book IS NULL)")
if not filters.get("show_cancelled_entries"):
conditions.append("is_cancelled = 0")

View File

@@ -157,12 +157,25 @@ def get_rootwise_opening_balances(filters, report_type):
if filters.project:
additional_conditions += " and project = %(project)s"
company_fb = frappe.db.get_value("Company", filters.company, "default_finance_book")
if filters.get("include_default_book_entries"):
additional_conditions += (
" AND (finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)"
)
if filters.get("finance_book"):
if company_fb and cstr(filters.get("finance_book")) != cstr(company_fb):
frappe.throw(
_("To use a different finance book, please uncheck 'Include Default Book Entries'")
)
else:
additional_conditions += (
" AND (finance_book in (%(finance_book)s, '') OR finance_book IS NULL)"
)
else:
additional_conditions += " AND (finance_book in (%(company_fb)s, '') OR finance_book IS NULL)"
else:
additional_conditions += " AND (finance_book in (%(finance_book)s, '') OR finance_book IS NULL)"
if filters.get("finance_book"):
additional_conditions += " AND (finance_book in (%(finance_book)s, '') OR finance_book IS NULL)"
else:
additional_conditions += " AND (finance_book in ('') OR finance_book IS NULL)"
accounting_dimensions = get_accounting_dimensions(as_list=False)
@@ -174,7 +187,7 @@ def get_rootwise_opening_balances(filters, report_type):
"year_start_date": filters.year_start_date,
"project": filters.project,
"finance_book": filters.finance_book,
"company_fb": frappe.db.get_value("Company", filters.company, "default_finance_book"),
"company_fb": company_fb,
}
if accounting_dimensions:

View File

@@ -33,7 +33,7 @@ frappe.listview_settings['Asset'] = {
}
},
onload: function(me) {
me.page.add_action_item('Make Asset Movement', function() {
me.page.add_action_item(__("Make Asset Movement"), function() {
const assets = me.get_checked_items();
frappe.call({
method: "erpnext.assets.doctype.asset.asset.make_asset_movement",

View File

@@ -133,6 +133,7 @@ def make_depreciation_entry(asset_name, date=None):
je.append("accounts", debit_entry)
je.flags.ignore_permissions = True
je.flags.planned_depr_entry = True
je.save()
if not je.meta.get_workflow():
je.submit()

View File

@@ -1421,7 +1421,7 @@ class TestDepreciationBasics(AssetSetup):
)
self.assertEqual(asset.status, "Submitted")
self.assertEqual(asset.get("value_after_depreciation"), 100000)
self.assertEqual(asset.get_value_after_depreciation(), 100000)
jv = make_journal_entry(
"_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False
@@ -1434,12 +1434,68 @@ class TestDepreciationBasics(AssetSetup):
jv.submit()
asset.reload()
self.assertEqual(asset.get("value_after_depreciation"), 99900)
self.assertEqual(asset.get_value_after_depreciation(), 99900)
jv.cancel()
asset.reload()
self.assertEqual(asset.get("value_after_depreciation"), 100000)
self.assertEqual(asset.get_value_after_depreciation(), 100000)
def test_manual_depreciation_for_depreciable_asset(self):
asset = create_asset(
item_code="Macbook Pro",
calculate_depreciation=1,
purchase_date="2020-01-30",
available_for_use_date="2020-01-30",
expected_value_after_useful_life=10000,
total_number_of_depreciations=10,
frequency_of_depreciation=1,
submit=1,
)
self.assertEqual(asset.status, "Submitted")
self.assertEqual(asset.get_value_after_depreciation(), 100000)
jv = make_journal_entry(
"_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False
)
for d in jv.accounts:
d.reference_type = "Asset"
d.reference_name = asset.name
jv.voucher_type = "Depreciation Entry"
jv.insert()
jv.submit()
asset.reload()
self.assertEqual(asset.get_value_after_depreciation(), 99900)
jv.cancel()
asset.reload()
self.assertEqual(asset.get_value_after_depreciation(), 100000)
def test_manual_depreciation_with_incorrect_jv_voucher_type(self):
asset = create_asset(
item_code="Macbook Pro",
calculate_depreciation=1,
purchase_date="2020-01-30",
available_for_use_date="2020-01-30",
expected_value_after_useful_life=10000,
total_number_of_depreciations=10,
frequency_of_depreciation=1,
submit=1,
)
jv = make_journal_entry(
"_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False
)
for d in jv.accounts:
d.reference_type = "Asset"
d.reference_name = asset.name
d.account_type = "Depreciation"
jv.voucher_type = "Journal Entry"
self.assertRaises(frappe.ValidationError, jv.insert)
def create_asset_data():

View File

@@ -119,7 +119,9 @@ class AssetValueAdjustment(Document):
if d.depreciation_method in ("Straight Line", "Manual"):
end_date = max(s.schedule_date for s in asset.schedules if cint(s.finance_book_id) == d.idx)
total_days = date_diff(end_date, self.date)
rate_per_day = flt(d.value_after_depreciation) / flt(total_days)
rate_per_day = flt(d.value_after_depreciation - d.expected_value_after_useful_life) / flt(
total_days
)
from_date = self.date
else:
no_of_depreciations = len(

View File

@@ -24,7 +24,7 @@ frappe.query_reports["Fixed Asset Register"] = {
"label": __("Period Based On"),
"fieldtype": "Select",
"options": ["Fiscal Year", "Date Range"],
"default": ["Fiscal Year"],
"default": "Fiscal Year",
"reqd": 1
},
{
@@ -75,12 +75,6 @@ frappe.query_reports["Fixed Asset Register"] = {
fieldtype: "Link",
options: "Asset Category"
},
{
fieldname:"finance_book",
label: __("Finance Book"),
fieldtype: "Link",
options: "Finance Book"
},
{
fieldname:"cost_center",
label: __("Cost Center"),
@@ -96,8 +90,20 @@ frappe.query_reports["Fixed Asset Register"] = {
reqd: 1
},
{
fieldname:"is_existing_asset",
label: __("Is Existing Asset"),
fieldname:"finance_book",
label: __("Finance Book"),
fieldtype: "Link",
options: "Finance Book",
depends_on: "eval: doc.filter_by_finance_book == 1",
},
{
fieldname:"filter_by_finance_book",
label: __("Filter by Finance Book"),
fieldtype: "Check"
},
{
fieldname:"only_existing_assets",
label: __("Only existing assets"),
fieldtype: "Check"
},
]

View File

@@ -45,8 +45,8 @@ def get_conditions(filters):
filters.year_end_date = getdate(fiscal_year.year_end_date)
conditions[date_field] = ["between", [filters.year_start_date, filters.year_end_date]]
if filters.get("is_existing_asset"):
conditions["is_existing_asset"] = filters.get("is_existing_asset")
if filters.get("only_existing_assets"):
conditions["is_existing_asset"] = filters.get("only_existing_assets")
if filters.get("asset_category"):
conditions["asset_category"] = filters.get("asset_category")
if filters.get("cost_center"):
@@ -102,19 +102,18 @@ def get_data(filters):
]
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
assets_linked_to_fb = frappe.db.get_all(
doctype="Asset Finance Book",
filters={"finance_book": filters.finance_book or ("is", "not set")},
pluck="parent",
)
assets_linked_to_fb = None
if filters.filter_by_finance_book:
assets_linked_to_fb = frappe.db.get_all(
doctype="Asset Finance Book",
filters={"finance_book": filters.finance_book or ("is", "not set")},
pluck="parent",
)
for asset in assets_record:
if filters.finance_book:
if asset.asset_id not in assets_linked_to_fb:
continue
else:
if asset.calculate_depreciation and asset.asset_id not in assets_linked_to_fb:
continue
if assets_linked_to_fb and asset.asset_id not in assets_linked_to_fb:
continue
asset_value = get_asset_value_after_depreciation(asset.asset_id, filters.finance_book)
row = {
@@ -172,11 +171,11 @@ def prepare_chart_data(data, filters):
"datasets": [
{
"name": _("Asset Value"),
"values": [d.get("asset_value") for d in labels_values_map.values()],
"values": [flt(d.get("asset_value"), 2) for d in labels_values_map.values()],
},
{
"name": _("Depreciatied Amount"),
"values": [d.get("depreciated_amount") for d in labels_values_map.values()],
"values": [flt(d.get("depreciated_amount"), 2) for d in labels_values_map.values()],
},
],
},
@@ -310,7 +309,7 @@ def get_columns(filters):
return [
{
"label": _("Asset Id"),
"label": _("Asset ID"),
"fieldtype": "Link",
"fieldname": "asset_id",
"options": "Asset",

View File

@@ -199,7 +199,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
);
}
if(flt(doc.per_billed)==0) {
if(flt(doc.per_billed) < 100) {
this.frm.add_custom_button(__('Payment Request'),
function() { me.make_payment_request() }, __('Create'));
}

View File

@@ -5,7 +5,7 @@
import json
import frappe
from frappe import _, throw
from frappe import _, bold, throw
from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied
from frappe.query_builder.functions import Sum
from frappe.utils import (
@@ -256,8 +256,8 @@ class AccountsController(TransactionBase):
self.validate_payment_schedule_dates()
self.set_due_date()
self.set_payment_schedule()
self.validate_payment_schedule_amount()
if not self.get("ignore_default_payment_terms_template"):
self.validate_payment_schedule_amount()
self.validate_due_date()
self.validate_advance_entries()
@@ -388,6 +388,15 @@ class AccountsController(TransactionBase):
msg += _("Please create purchase from internal sale or delivery document itself")
frappe.throw(msg, title=_("Internal Sales Reference Missing"))
label = "Delivery Note Item" if self.doctype == "Purchase Receipt" else "Sales Invoice Item"
field = frappe.scrub(label)
for row in self.get("items"):
if not row.get(field):
msg = f"At Row {row.idx}: The field {bold(label)} is mandatory for internal transfer"
frappe.throw(_(msg), title=_("Internal Transfer Reference Missing"))
def disable_pricing_rule_on_internal_transfer(self):
if not self.get("ignore_pricing_rule") and self.is_internal_transfer():
self.ignore_pricing_rule = 1
@@ -1577,6 +1586,7 @@ class AccountsController(TransactionBase):
base_grand_total = self.get("base_rounded_total") or self.base_grand_total
grand_total = self.get("rounded_total") or self.grand_total
automatically_fetch_payment_terms = 0
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
base_grand_total = base_grand_total - flt(self.base_write_off_amount)
@@ -1622,19 +1632,23 @@ class AccountsController(TransactionBase):
)
self.append("payment_schedule", data)
for d in self.get("payment_schedule"):
if d.invoice_portion:
d.payment_amount = flt(
grand_total * flt(d.invoice_portion / 100), d.precision("payment_amount")
)
d.base_payment_amount = flt(
base_grand_total * flt(d.invoice_portion / 100), d.precision("base_payment_amount")
)
d.outstanding = d.payment_amount
elif not d.invoice_portion:
d.base_payment_amount = flt(
d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount")
)
if not (
automatically_fetch_payment_terms
and self.linked_order_has_payment_terms(po_or_so, fieldname, doctype)
):
for d in self.get("payment_schedule"):
if d.invoice_portion:
d.payment_amount = flt(
grand_total * flt(d.invoice_portion / 100), d.precision("payment_amount")
)
d.base_payment_amount = flt(
base_grand_total * flt(d.invoice_portion / 100), d.precision("base_payment_amount")
)
d.outstanding = d.payment_amount
elif not d.invoice_portion:
d.base_payment_amount = flt(
d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount")
)
def get_order_details(self):
if self.doctype == "Sales Invoice":
@@ -1687,6 +1701,10 @@ class AccountsController(TransactionBase):
"invoice_portion": schedule.invoice_portion,
"mode_of_payment": schedule.mode_of_payment,
"description": schedule.description,
"payment_amount": schedule.payment_amount,
"base_payment_amount": schedule.base_payment_amount,
"outstanding": schedule.outstanding,
"paid_amount": schedule.paid_amount,
}
if schedule.discount_type == "Percentage":

View File

@@ -165,6 +165,7 @@
"fieldname": "slide_3_content_align",
"fieldtype": "Select",
"label": "Content Align",
"options": "Left\nCentre\nRight",
"reqd": 0
},
{
@@ -214,6 +215,7 @@
"fieldname": "slide_4_content_align",
"fieldtype": "Select",
"label": "Content Align",
"options": "Left\nCentre\nRight",
"reqd": 0
},
{
@@ -263,6 +265,7 @@
"fieldname": "slide_5_content_align",
"fieldtype": "Select",
"label": "Content Align",
"options": "Left\nCentre\nRight",
"reqd": 0
},
{
@@ -274,7 +277,7 @@
}
],
"idx": 2,
"modified": "2021-02-24 15:57:05.889709",
"modified": "2023-05-12 15:03:57.604060",
"modified_by": "Administrator",
"module": "E-commerce",
"name": "Hero Slider",

View File

@@ -592,20 +592,18 @@ erpnext.work_order = {
// all materials transferred for manufacturing, make this primary
finish_btn.addClass('btn-primary');
}
} else {
frappe.db.get_doc("Manufacturing Settings").then((doc) => {
let allowance_percentage = doc.overproduction_percentage_for_work_order;
} else if (frm.doc.__onload && frm.doc.__onload.overproduction_percentage) {
let allowance_percentage = frm.doc.__onload.overproduction_percentage;
if (allowance_percentage > 0) {
let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty);
if (allowance_percentage > 0) {
let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty);
if ((flt(doc.produced_qty) < allowed_qty)) {
frm.add_custom_button(__('Finish'), function() {
erpnext.work_order.make_se(frm, 'Manufacture');
});
}
if ((flt(doc.produced_qty) < allowed_qty)) {
frm.add_custom_button(__('Finish'), function() {
erpnext.work_order.make_se(frm, 'Manufacture');
});
}
});
}
}
}
} else {

View File

@@ -69,7 +69,7 @@ def get_columns(filters):
"label": _("Id"),
"fieldname": "name",
"fieldtype": "Link",
"options": "Work Order",
"options": "Quality Inspection",
"width": 100,
},
{"label": _("Report Date"), "fieldname": "report_date", "fieldtype": "Date", "width": 150},

View File

@@ -67,7 +67,6 @@
"label": "Disabled"
},
{
"depends_on": "allow_tax_exemption",
"fieldname": "standard_tax_exemption_amount",
"fieldtype": "Currency",
"label": "Standard Tax Exemption Amount",
@@ -104,7 +103,7 @@
],
"is_submittable": 1,
"links": [],
"modified": "2021-03-31 22:42:08.139520",
"modified": "2023-05-01 13:42:08.139520",
"modified_by": "Administrator",
"module": "Payroll",
"name": "Income Tax Slab",

View File

@@ -80,22 +80,27 @@ frappe.ui.form.on("Salary Slip", {
},
currency: function(frm) {
frm.trigger("update_currency_changes");
},
update_currency_changes: function(frm) {
frm.trigger("set_exchange_rate");
frm.trigger("set_dynamic_labels");
},
set_dynamic_labels: function(frm) {
var company_currency = frm.doc.company? erpnext.get_currency(frm.doc.company): frappe.defaults.get_default("currency");
if (frm.doc.employee && frm.doc.currency) {
frappe.run_serially([
() => frm.events.set_exchange_rate(frm, company_currency),
() => frm.events.change_form_labels(frm, company_currency),
() => frm.events.change_grid_labels(frm),
() => frm.refresh_fields()
() => frm.events.change_form_labels(frm),
() => frm.events.change_grid_labels(frm),
() => frm.refresh_fields()
]);
}
},
set_exchange_rate: function(frm, company_currency) {
set_exchange_rate: function(frm) {
const company_currency = erpnext.get_currency(frm.doc.company);
if (frm.doc.docstatus === 0) {
if (frm.doc.currency) {
var from_currency = frm.doc.currency;
@@ -133,9 +138,11 @@ frappe.ui.form.on("Salary Slip", {
frm.set_df_property('section_break_43', 'hidden', 1);
},
change_form_labels: function(frm, company_currency) {
change_form_labels: function(frm) {
const company_currency = erpnext.get_currency(frm.doc.company);
frm.set_currency_labels(["base_hour_rate", "base_gross_pay", "base_total_deduction",
"base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date", "gross_base_year_to_date"],
"base_net_pay", "base_rounded_total", "base_total_in_words", "base_year_to_date", "base_month_to_date", "base_gross_year_to_date"],
company_currency);
frm.set_currency_labels(["hour_rate", "gross_pay", "total_deduction", "net_pay", "rounded_total", "total_in_words", "year_to_date", "month_to_date", "gross_year_to_date"],
@@ -207,6 +214,9 @@ frappe.ui.form.on("Salary Slip", {
frm.fields_dict.absent_days.set_description(__("Unmarked Days is treated as {0}. You can can change this in {1}", [r.message, frappe.utils.get_form_link("Payroll Settings", "Payroll Settings", true)]));
}
frm.refresh();
// triggering events explicitly because structure is set on the server-side
// and currency is fetched from the structure
frm.trigger("update_currency_changes");
}
});
}

View File

@@ -653,15 +653,17 @@ class SalarySlip(TransactionBase):
amount = self.eval_condition_and_formula(struct_row, data)
if struct_row.statistical_component:
default_data[struct_row.abbr] = amount
# update statitical component amount in reference data based on payment days
# since row for statistical component is not added to salary slip
if struct_row.depends_on_payment_days:
joining_date, relieving_date = self.get_joining_and_relieving_dates()
default_data[struct_row.abbr] = amount
data[struct_row.abbr] = flt(
(flt(amount) * flt(self.payment_days) / cint(self.total_working_days)),
struct_row.precision("amount"),
payment_days_amount = (
flt(amount) * flt(self.payment_days) / cint(self.total_working_days)
if self.total_working_days
else 0
)
data[struct_row.abbr] = payment_days_amount
elif amount or struct_row.amount_based_on_formula and amount is not None:
default_amount = self.eval_condition_and_formula(struct_row, default_data)
@@ -1331,6 +1333,7 @@ class SalarySlip(TransactionBase):
if declaration:
total_exemption_amount = declaration
if tax_slab.standard_tax_exemption_amount:
total_exemption_amount += flt(tax_slab.standard_tax_exemption_amount)
return total_exemption_amount

View File

@@ -8,222 +8,325 @@ from frappe.utils import flt
import erpnext
salary_slip = frappe.qb.DocType("Salary Slip")
salary_detail = frappe.qb.DocType("Salary Detail")
salary_component = frappe.qb.DocType("Salary Component")
def execute(filters=None):
if not filters:
filters = {}
currency = None
if filters.get("currency"):
currency = filters.get("currency")
company_currency = erpnext.get_company_currency(filters.get("company"))
salary_slips = get_salary_slips(filters, company_currency)
if not salary_slips:
return [], []
columns, earning_types, ded_types = get_columns(salary_slips)
ss_earning_map = get_ss_earning_map(salary_slips, currency, company_currency)
ss_ded_map = get_ss_ded_map(salary_slips, currency, company_currency)
earning_types, ded_types = get_earning_and_deduction_types(salary_slips)
columns = get_columns(earning_types, ded_types)
ss_earning_map = get_salary_slip_details(salary_slips, currency, company_currency, "earnings")
ss_ded_map = get_salary_slip_details(salary_slips, currency, company_currency, "deductions")
doj_map = get_employee_doj_map()
data = []
for ss in salary_slips:
row = [
ss.name,
ss.employee,
ss.employee_name,
doj_map.get(ss.employee),
ss.branch,
ss.department,
ss.designation,
ss.company,
ss.start_date,
ss.end_date,
ss.leave_without_pay,
ss.payment_days,
]
row = {
"salary_slip_id": ss.name,
"employee": ss.employee,
"employee_name": ss.employee_name,
"data_of_joining": doj_map.get(ss.employee),
"branch": ss.branch,
"department": ss.department,
"designation": ss.designation,
"company": ss.company,
"start_date": ss.start_date,
"end_date": ss.end_date,
"leave_without_pay": ss.leave_without_pay,
"payment_days": ss.payment_days,
"currency": currency or company_currency,
"total_loan_repayment": ss.total_loan_repayment,
}
if ss.branch is not None:
columns[3] = columns[3].replace("-1", "120")
if ss.department is not None:
columns[4] = columns[4].replace("-1", "120")
if ss.designation is not None:
columns[5] = columns[5].replace("-1", "120")
if ss.leave_without_pay is not None:
columns[9] = columns[9].replace("-1", "130")
update_column_width(ss, columns)
for e in earning_types:
row.append(ss_earning_map.get(ss.name, {}).get(e))
if currency == company_currency:
row += [flt(ss.gross_pay) * flt(ss.exchange_rate)]
else:
row += [ss.gross_pay]
row.update({frappe.scrub(e): ss_earning_map.get(ss.name, {}).get(e)})
for d in ded_types:
row.append(ss_ded_map.get(ss.name, {}).get(d))
row.append(ss.total_loan_repayment)
row.update({frappe.scrub(d): ss_ded_map.get(ss.name, {}).get(d)})
if currency == company_currency:
row += [
flt(ss.total_deduction) * flt(ss.exchange_rate),
flt(ss.net_pay) * flt(ss.exchange_rate),
]
row.update(
{
"gross_pay": flt(ss.gross_pay) * flt(ss.exchange_rate),
"total_deduction": flt(ss.total_deduction) * flt(ss.exchange_rate),
"net_pay": flt(ss.net_pay) * flt(ss.exchange_rate),
}
)
else:
row += [ss.total_deduction, ss.net_pay]
row.append(currency or company_currency)
row.update(
{"gross_pay": ss.gross_pay, "total_deduction": ss.total_deduction, "net_pay": ss.net_pay}
)
data.append(row)
return columns, data
def get_columns(salary_slips):
"""
def get_earning_and_deduction_types(salary_slips):
salary_component_and_type = {_("Earning"): [], _("Deduction"): []}
for salary_compoent in get_salary_components(salary_slips):
component_type = get_salary_component_type(salary_compoent[0])
salary_component_and_type[_(component_type)].append(salary_compoent[0])
return sorted(salary_component_and_type[_("Earning")]), sorted(
salary_component_and_type[_("Deduction")]
)
def update_column_width(ss, columns):
if ss.branch is not None:
columns[3].update({"width": 120})
if ss.department is not None:
columns[4].update({"width": 120})
if ss.designation is not None:
columns[5].update({"width": 120})
if ss.leave_without_pay is not None:
columns[9].update({"width": 120})
def get_columns(earning_types, ded_types):
columns = [
_("Salary Slip ID") + ":Link/Salary Slip:150",
_("Employee") + ":Link/Employee:120",
_("Employee Name") + "::140",
_("Date of Joining") + "::80",
_("Branch") + ":Link/Branch:120",
_("Department") + ":Link/Department:120",
_("Designation") + ":Link/Designation:120",
_("Company") + ":Link/Company:120",
_("Start Date") + "::80",
_("End Date") + "::80",
_("Leave Without Pay") + ":Float:130",
_("Payment Days") + ":Float:120",
_("Currency") + ":Link/Currency:80"
]
"""
columns = [
_("Salary Slip ID") + ":Link/Salary Slip:150",
_("Employee") + ":Link/Employee:120",
_("Employee Name") + "::140",
_("Date of Joining") + "::80",
_("Branch") + ":Link/Branch:-1",
_("Department") + ":Link/Department:-1",
_("Designation") + ":Link/Designation:120",
_("Company") + ":Link/Company:120",
_("Start Date") + "::80",
_("End Date") + "::80",
_("Leave Without Pay") + ":Float:50",
_("Payment Days") + ":Float:120",
{
"label": _("Salary Slip ID"),
"fieldname": "salary_slip_id",
"fieldtype": "Link",
"options": "Salary Slip",
"width": 150,
},
{
"label": _("Employee"),
"fieldname": "employee",
"fieldtype": "Link",
"options": "Employee",
"width": 120,
},
{
"label": _("Employee Name"),
"fieldname": "employee_name",
"fieldtype": "Data",
"width": 140,
},
{
"label": _("Date of Joining"),
"fieldname": "data_of_joining",
"fieldtype": "Date",
"width": 80,
},
{
"label": _("Branch"),
"fieldname": "branch",
"fieldtype": "Link",
"options": "Branch",
"width": -1,
},
{
"label": _("Department"),
"fieldname": "department",
"fieldtype": "Link",
"options": "Department",
"width": -1,
},
{
"label": _("Designation"),
"fieldname": "designation",
"fieldtype": "Link",
"options": "Designation",
"width": 120,
},
{
"label": _("Company"),
"fieldname": "company",
"fieldtype": "Link",
"options": "Company",
"width": 120,
},
{
"label": _("Start Date"),
"fieldname": "start_date",
"fieldtype": "Data",
"width": 80,
},
{
"label": _("End Date"),
"fieldname": "end_date",
"fieldtype": "Data",
"width": 80,
},
{
"label": _("Leave Without Pay"),
"fieldname": "leave_without_pay",
"fieldtype": "Float",
"width": 50,
},
{
"label": _("Payment Days"),
"fieldname": "payment_days",
"fieldtype": "Float",
"width": 120,
},
{
"label": _("Currency"),
"fieldname": "currency",
"fieldtype": "Link",
"options": "Currency",
"hidden": 1,
},
]
salary_components = {_("Earning"): [], _("Deduction"): []}
for earning in earning_types:
columns.append(
{
"label": earning,
"fieldname": frappe.scrub(earning),
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
)
for component in frappe.db.sql(
"""select distinct sd.salary_component, sc.type
from `tabSalary Detail` sd, `tabSalary Component` sc
where sc.name=sd.salary_component and sd.amount != 0 and sd.parent in (%s)"""
% (", ".join(["%s"] * len(salary_slips))),
tuple([d.name for d in salary_slips]),
as_dict=1,
):
salary_components[_(component.type)].append(component.salary_component)
columns.append(
{
"label": _("Gross Pay"),
"fieldname": "gross_pay",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
)
columns = (
columns
+ [(e + ":Currency:120") for e in salary_components[_("Earning")]]
+ [_("Gross Pay") + ":Currency:120"]
+ [(d + ":Currency:120") for d in salary_components[_("Deduction")]]
+ [
_("Loan Repayment") + ":Currency:120",
_("Total Deduction") + ":Currency:120",
_("Net Pay") + ":Currency:120",
for deduction in ded_types:
columns.append(
{
"label": deduction,
"fieldname": frappe.scrub(deduction),
"fieldtype": "Currency",
"options": "currency",
"width": 120,
}
)
columns.extend(
[
{
"label": _("Loan Repayment"),
"fieldname": "total_loan_repayment",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Total Deduction"),
"fieldname": "total_deduction",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Net Pay"),
"fieldname": "net_pay",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
]
)
return columns, salary_components[_("Earning")], salary_components[_("Deduction")]
return columns
def get_salary_components(salary_slips):
return (
frappe.qb.from_(salary_detail)
.where((salary_detail.amount != 0) & (salary_detail.parent.isin([d.name for d in salary_slips])))
.select(salary_detail.salary_component)
.distinct()
).run(as_list=True)
def get_salary_component_type(salary_component):
return frappe.db.get_value("Salary Component", salary_component, "type", cache=True)
def get_salary_slips(filters, company_currency):
filters.update({"from_date": filters.get("from_date"), "to_date": filters.get("to_date")})
conditions, filters = get_conditions(filters, company_currency)
salary_slips = frappe.db.sql(
"""select * from `tabSalary Slip` where %s
order by employee"""
% conditions,
filters,
as_dict=1,
)
doc_status = {"Draft": 0, "Submitted": 1, "Cancelled": 2}
query = frappe.qb.from_(salary_slip).select(salary_slip.star)
if filters.get("docstatus"):
query = query.where(salary_slip.docstatus == doc_status[filters.get("docstatus")])
if filters.get("from_date"):
query = query.where(salary_slip.start_date >= filters.get("from_date"))
if filters.get("to_date"):
query = query.where(salary_slip.end_date <= filters.get("to_date"))
if filters.get("company"):
query = query.where(salary_slip.company == filters.get("company"))
if filters.get("employee"):
query = query.where(salary_slip.employee == filters.get("employee"))
if filters.get("currency") and filters.get("currency") != company_currency:
query = query.where(salary_slip.currency == filters.get("currency"))
salary_slips = query.run(as_dict=1)
return salary_slips or []
def get_conditions(filters, company_currency):
conditions = ""
doc_status = {"Draft": 0, "Submitted": 1, "Cancelled": 2}
if filters.get("docstatus"):
conditions += "docstatus = {0}".format(doc_status[filters.get("docstatus")])
if filters.get("from_date"):
conditions += " and start_date >= %(from_date)s"
if filters.get("to_date"):
conditions += " and end_date <= %(to_date)s"
if filters.get("company"):
conditions += " and company = %(company)s"
if filters.get("employee"):
conditions += " and employee = %(employee)s"
if filters.get("currency") and filters.get("currency") != company_currency:
conditions += " and currency = %(currency)s"
return conditions, filters
def get_employee_doj_map():
return frappe._dict(
frappe.db.sql(
"""
SELECT
employee,
date_of_joining
FROM `tabEmployee`
"""
employee = frappe.qb.DocType("Employee")
result = (frappe.qb.from_(employee).select(employee.name, employee.date_of_joining)).run()
return frappe._dict(result)
def get_salary_slip_details(salary_slips, currency, company_currency, component_type):
salary_slips = [ss.name for ss in salary_slips]
result = (
frappe.qb.from_(salary_slip)
.join(salary_detail)
.on(salary_slip.name == salary_detail.parent)
.where((salary_detail.parent.isin(salary_slips)) & (salary_detail.parentfield == component_type))
.select(
salary_detail.parent,
salary_detail.salary_component,
salary_detail.amount,
salary_slip.exchange_rate,
)
)
).run(as_dict=1)
ss_map = {}
def get_ss_earning_map(salary_slips, currency, company_currency):
ss_earnings = frappe.db.sql(
"""select sd.parent, sd.salary_component, sd.amount, ss.exchange_rate, ss.name
from `tabSalary Detail` sd, `tabSalary Slip` ss where sd.parent=ss.name and sd.parent in (%s)"""
% (", ".join(["%s"] * len(salary_slips))),
tuple([d.name for d in salary_slips]),
as_dict=1,
)
ss_earning_map = {}
for d in ss_earnings:
ss_earning_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0)
for d in result:
ss_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0)
if currency == company_currency:
ss_earning_map[d.parent][d.salary_component] += flt(d.amount) * flt(
ss_map[d.parent][d.salary_component] += flt(d.amount) * flt(
d.exchange_rate if d.exchange_rate else 1
)
else:
ss_earning_map[d.parent][d.salary_component] += flt(d.amount)
ss_map[d.parent][d.salary_component] += flt(d.amount)
return ss_earning_map
def get_ss_ded_map(salary_slips, currency, company_currency):
ss_deductions = frappe.db.sql(
"""select sd.parent, sd.salary_component, sd.amount, ss.exchange_rate, ss.name
from `tabSalary Detail` sd, `tabSalary Slip` ss where sd.parent=ss.name and sd.parent in (%s)"""
% (", ".join(["%s"] * len(salary_slips))),
tuple([d.name for d in salary_slips]),
as_dict=1,
)
ss_ded_map = {}
for d in ss_deductions:
ss_ded_map.setdefault(d.parent, frappe._dict()).setdefault(d.salary_component, 0.0)
if currency == company_currency:
ss_ded_map[d.parent][d.salary_component] += flt(d.amount) * flt(
d.exchange_rate if d.exchange_rate else 1
)
else:
ss_ded_map[d.parent][d.salary_component] += flt(d.amount)
return ss_ded_map
return ss_map

View File

@@ -297,8 +297,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
});
},
make_payment_request: function() {
var me = this;
make_payment_request() {
let me = this;
const payment_request_type = (in_list(['Sales Order', 'Sales Invoice'], this.frm.doc.doctype))
? "Inward" : "Outward";
@@ -314,7 +314,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
},
callback: function(r) {
if(!r.exc){
var doc = frappe.model.sync(r.message);
frappe.model.sync(r.message);
frappe.set_route("Form", r.message.doctype, r.message.name);
}
}
@@ -2011,7 +2011,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
},
prompt_user_for_reference_date(){
var me = this;
let me = this;
frappe.prompt({
label: __("Cheque/Reference Date"),
fieldname: "reference_date",
@@ -2038,7 +2038,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
let has_payment_schedule = this.frm.doc.payment_schedule && this.frm.doc.payment_schedule.length;
if(!is_eligible || !has_payment_schedule) return false;
let has_discount = this.frm.doc.payment_schedule.some(row => row.discount_date);
let has_discount = this.frm.doc.payment_schedule.some(row => row.discount);
return has_discount;
},

View File

@@ -1389,8 +1389,9 @@ def get_work_order_items(sales_order, for_raw_material_request=0):
.select(Sum(wo.qty))
.where(
(wo.production_item == i.item_code)
& (wo.sales_order == so.name) * (wo.sales_order_item == i.name)
& (wo.docstatus.lte(2))
& (wo.sales_order == so.name)
& (wo.sales_order_item == i.name)
& (wo.docstatus.lt(2))
)
.run()[0][0]
)

View File

@@ -37,8 +37,24 @@ class ItemGroup(NestedSet, WebsiteGenerator):
self.make_route()
self.validate_item_group_defaults()
self.check_item_tax()
ECommerceSettings.validate_field_filters(self.filter_fields, enable_field_filters=True)
def check_item_tax(self):
"""Check whether Tax Rate is not entered twice for same Tax Type"""
check_list = []
for d in self.get("taxes"):
if d.item_tax_template:
if (d.item_tax_template, d.tax_category) in check_list:
frappe.throw(
_("{0} entered twice {1} in Item Taxes").format(
frappe.bold(d.item_tax_template),
"for tax category {0}".format(frappe.bold(d.tax_category)) if d.tax_category else "",
)
)
else:
check_list.append((d.item_tax_template, d.tax_category))
def on_update(self):
NestedSet.on_update(self)
invalidate_cache_for(self)

View File

@@ -156,7 +156,7 @@ def add_standard_navbar_items():
{
"item_label": "User Forum",
"item_type": "Route",
"route": "https://discuss.erpnext.com",
"route": "https://discuss.frappe.io",
"is_standard": 1,
},
{

View File

@@ -354,10 +354,15 @@ class Item(Document):
check_list = []
for d in self.get("taxes"):
if d.item_tax_template:
if d.item_tax_template in check_list:
frappe.throw(_("{0} entered twice in Item Tax").format(d.item_tax_template))
if (d.item_tax_template, d.tax_category) in check_list:
frappe.throw(
_("{0} entered twice {1} in Item Taxes").format(
frappe.bold(d.item_tax_template),
"for tax category {0}".format(frappe.bold(d.tax_category)) if d.tax_category else "",
)
)
else:
check_list.append(d.item_tax_template)
check_list.append((d.item_tax_template, d.tax_category))
def validate_barcode(self):
from stdnum import ean

View File

@@ -369,8 +369,20 @@ class PurchaseReceipt(BuyingController):
)
outgoing_amount = d.base_net_amount
if self.is_internal_supplier and d.valuation_rate:
outgoing_amount = d.valuation_rate * d.stock_qty
if self.is_internal_transfer() and d.valuation_rate:
outgoing_amount = abs(
frappe.db.get_value(
"Stock Ledger Entry",
{
"voucher_type": "Purchase Receipt",
"voucher_no": self.name,
"voucher_detail_no": d.name,
"warehouse": d.from_warehouse,
"is_cancelled": 0,
},
"stock_value_difference",
)
)
credit_amount = outgoing_amount
if credit_amount:

View File

@@ -1707,6 +1707,148 @@ class TestPurchaseReceipt(FrappeTestCase):
self.assertTrue(return_pi.docstatus == 1)
def test_internal_pr_gl_entries(self):
from erpnext.stock import get_warehouse_account_map
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
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
create_stock_reconciliation,
)
prepare_data_for_internal_transfer()
customer = "_Test Internal Customer 2"
company = "_Test Company with perpetual inventory"
from_warehouse = create_warehouse("_Test Internal From Warehouse New", company=company)
target_warehouse = create_warehouse("_Test Internal GIT Warehouse New", company=company)
to_warehouse = create_warehouse("_Test Internal To Warehouse New", company=company)
item = make_item(properties={"is_stock_item": 1, "valuation_rate": 100})
make_stock_entry(
purpose="Material Receipt",
item_code=item.name,
qty=10,
company=company,
to_warehouse=from_warehouse,
posting_date=add_days(today(), -3),
)
# Step - 1: Create Delivery Note with Internal Customer
dn = create_delivery_note(
item_code=item.name,
company=company,
customer=customer,
cost_center="Main - TCP1",
expense_account="Cost of Goods Sold - TCP1",
qty=10,
rate=100,
warehouse=from_warehouse,
target_warehouse=target_warehouse,
posting_date=add_days(today(), -2),
)
# Step - 2: Create Internal Purchase Receipt
pr = make_inter_company_purchase_receipt(dn.name)
pr.items[0].qty = 10
pr.items[0].from_warehouse = target_warehouse
pr.items[0].warehouse = to_warehouse
pr.items[0].rejected_warehouse = from_warehouse
pr.save()
pr.submit()
# Step - 3: Create back-date Stock Reconciliation [After DN and Before PR]
create_stock_reconciliation(
item_code=item.name,
warehouse=target_warehouse,
qty=10,
rate=50,
company=company,
posting_date=add_days(today(), -1),
expense_account="Stock Adjustment - TCP1",
)
warehouse_account = get_warehouse_account_map(company)
stock_account_value = frappe.db.get_value(
"GL Entry",
{
"account": warehouse_account[target_warehouse]["account"],
"voucher_type": "Purchase Receipt",
"voucher_no": pr.name,
"is_cancelled": 0,
},
fieldname=["credit"],
)
stock_diff = frappe.db.get_value(
"Stock Ledger Entry",
{
"voucher_type": "Purchase Receipt",
"voucher_no": pr.name,
"is_cancelled": 0,
},
fieldname=["sum(stock_value_difference)"],
)
# Value of Stock Account should be equal to the sum of Stock Value Difference
self.assertEqual(stock_account_value, stock_diff)
def test_internal_pr_reference(self):
item = make_item(properties={"is_stock_item": 1, "valuation_rate": 100})
customer = "_Test Internal Customer 2"
company = "_Test Company with perpetual inventory"
from_warehouse = create_warehouse("_Test Internal From Warehouse New 1", company=company)
target_warehouse = create_warehouse("_Test Internal GIT Warehouse New 1", company=company)
to_warehouse = create_warehouse("_Test Internal To Warehouse New 1", company=company)
# Step 2: Create Stock Entry (Material Receipt)
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
make_stock_entry(
purpose="Material Receipt",
item_code=item.name,
qty=15,
company=company,
to_warehouse=from_warehouse,
)
# Step 3: Create Delivery Note with Internal Customer
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
dn = create_delivery_note(
item_code=item.name,
company=company,
customer=customer,
cost_center="Main - TCP1",
expense_account="Cost of Goods Sold - TCP1",
qty=10,
rate=100,
warehouse=from_warehouse,
target_warehouse=target_warehouse,
)
# Step 4: Create Internal Purchase Receipt
from erpnext.controllers.status_updater import OverAllowanceError
from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt
pr = make_inter_company_purchase_receipt(dn.name)
pr.inter_company_reference = ""
self.assertRaises(frappe.ValidationError, pr.save)
pr.inter_company_reference = dn.name
pr.items[0].qty = 10
pr.items[0].from_warehouse = target_warehouse
pr.items[0].warehouse = to_warehouse
pr.items[0].rejected_warehouse = from_warehouse
pr.save()
delivery_note_item = pr.items[0].delivery_note_item
pr.items[0].delivery_note_item = ""
self.assertRaises(frappe.ValidationError, pr.save)
pr.load_from_db()
pr.items[0].delivery_note_item = delivery_note_item
pr.save()
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier

View File

@@ -5,7 +5,8 @@ import frappe
from frappe import _
from frappe.exceptions import QueryDeadlockError, QueryTimeoutError
from frappe.model.document import Document
from frappe.utils import cint, get_link_to_form, get_weekday, now, nowtime
from frappe.query_builder.functions import Max
from frappe.utils import cint, get_link_to_form, get_weekday, getdate, now, nowtime
from frappe.utils.user import get_users_with_role
from rq.timeouts import JobTimeoutException
@@ -22,9 +23,57 @@ RecoverableErrors = (JobTimeoutException, QueryDeadlockError, QueryTimeoutError)
class RepostItemValuation(Document):
def validate(self):
self.validate_period_closing_voucher()
self.set_status(write=False)
self.reset_field_values()
self.set_company()
self.validate_accounts_freeze()
def validate_period_closing_voucher(self):
year_end_date = self.get_max_year_end_date(self.company)
if year_end_date and getdate(self.posting_date) <= getdate(year_end_date):
msg = f"Due to period closing, you cannot repost item valuation before {year_end_date}"
frappe.throw(_(msg))
@staticmethod
def get_max_year_end_date(company):
data = frappe.get_all(
"Period Closing Voucher", fields=["fiscal_year"], filters={"docstatus": 1, "company": company}
)
if not data:
return
fiscal_years = [d.fiscal_year for d in data]
table = frappe.qb.DocType("Fiscal Year")
query = (
frappe.qb.from_(table)
.select(Max(table.year_end_date))
.where((table.name.isin(fiscal_years)) & (table.disabled == 0))
).run()
return query[0][0] if query else None
def validate_accounts_freeze(self):
acc_settings = frappe.db.get_value(
"Accounts Settings",
"Accounts Settings",
["acc_frozen_upto", "frozen_accounts_modifier"],
as_dict=1,
)
if not acc_settings.acc_frozen_upto:
return
if getdate(self.posting_date) <= getdate(acc_settings.acc_frozen_upto):
if (
acc_settings.frozen_accounts_modifier
and frappe.session.user in get_users_with_role(acc_settings.frozen_accounts_modifier)
):
frappe.msgprint(_("Caution: This might alter frozen accounts."))
return
frappe.throw(
_("You cannot repost item valuation before {}").format(acc_settings.acc_frozen_upto)
)
def reset_field_values(self):
if self.based_on == "Transaction":
@@ -235,7 +284,7 @@ def _get_directly_dependent_vouchers(doc):
def notify_error_to_stock_managers(doc, traceback):
recipients = get_users_with_role("Stock Manager")
if not recipients:
get_users_with_role("System Manager")
recipients = get_users_with_role("System Manager")
subject = _("Error while reposting item valuation")
message = (

View File

@@ -272,3 +272,25 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin):
[{"credit": 50, "debit": 0}],
gle_filters={"account": "Stock In Hand - TCP1"},
)
def test_account_freeze_validation(self):
today = nowdate()
riv = frappe.get_doc(
doctype="Repost Item Valuation",
item_code="_Test Item",
warehouse="_Test Warehouse - _TC",
based_on="Item and Warehouse",
posting_date=today,
posting_time="00:01:00",
)
riv.flags.dont_run_in_test = True # keep it queued
accounts_settings = frappe.get_doc("Accounts Settings")
accounts_settings.acc_frozen_upto = today
accounts_settings.frozen_accounts_modifier = ""
accounts_settings.save()
self.assertRaises(frappe.ValidationError, riv.save)
accounts_settings.acc_frozen_upto = ""
accounts_settings.save()

View File

@@ -411,10 +411,10 @@
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"read_only": 1,
"remember_last_selected_value": 1,
"reqd": 1,
"search_index": 1
"search_index": 1,
"set_only_once": 1
},
{
"fieldname": "status",
@@ -434,7 +434,7 @@
"icon": "fa fa-barcode",
"idx": 1,
"links": [],
"modified": "2021-01-08 14:31:15.375996",
"modified": "2023-04-14 15:58:46.139887",
"modified_by": "Administrator",
"module": "Stock",
"name": "Serial No",
@@ -461,7 +461,6 @@
"read": 1,
"report": 1,
"role": "Stock Manager",
"set_user_permissions": 1,
"write": 1
},
{

View File

@@ -529,7 +529,9 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin):
# check if cancellation of stock reco is blocked
self.assertRaises(NegativeStockError, sr.cancel)
repost_exists = bool(frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name}))
repost_exists = bool(
frappe.db.exists("Repost Item Valuation", {"voucher_no": sr.name, "status": "Queued"})
)
self.assertFalse(repost_exists, msg="Negative stock validation not working on reco cancellation")
def test_intermediate_sr_bin_update(self):

View File

@@ -606,7 +606,9 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False):
taxes_with_no_validity.append(tax)
if taxes_with_validity:
taxes = sorted(taxes_with_validity, key=lambda i: i.valid_from, reverse=True)
taxes = sorted(
taxes_with_validity, key=lambda i: i.valid_from or tax.maximum_net_rate, reverse=True
)
else:
taxes = taxes_with_no_validity

View File

@@ -536,7 +536,7 @@ class update_entries_after(object):
sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
and sle.voucher_detail_no
and sle.actual_qty < 0
and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier")
and is_internal_transfer(sle)
):
sle.outgoing_rate = get_incoming_rate_for_inter_company_transfer(sle)
@@ -648,7 +648,7 @@ class update_entries_after(object):
elif (
sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
and sle.voucher_detail_no
and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier")
and is_internal_transfer(sle)
):
rate = get_incoming_rate_for_inter_company_transfer(sle)
else:
@@ -1488,3 +1488,15 @@ def get_incoming_rate_for_inter_company_transfer(sle) -> float:
)
return rate
def is_internal_transfer(sle):
data = frappe.get_cached_value(
sle.voucher_type,
sle.voucher_no,
["is_internal_supplier", "represents_company", "company"],
as_dict=True,
)
if data.is_internal_supplier and data.represents_company == data.company:
return True

View File

@@ -198,7 +198,7 @@
}
],
"links": [],
"modified": "2021-07-27 11:16:45.596579",
"modified": "2023-04-21 17:16:56.192560",
"modified_by": "Administrator",
"module": "Support",
"name": "Service Level Agreement",
@@ -217,19 +217,12 @@
"write": 1
},
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"share": 1,
"write": 1
"role": "All"
}
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}