Compare commits

..

150 Commits

Author SHA1 Message Date
Frappe PR Bot
07ff956fd8 chore(release): Bumped to Version 13.49.3
## [13.49.3](https://github.com/frappe/erpnext/compare/v13.49.2...v13.49.3) (2023-03-07)

### Performance Improvements

* `update_completed_qty()` in `material_request.py` ([6841e22](6841e22ffe))
* Stock Entry (Material Transfer) ([56a422d](56a422deed))
2023-03-07 17:01:01 +00:00
Sagar Sharma
c575942acf Merge pull request #34341 from frappe/mergify/bp/version-13/pr-34336
perf: Stock Entry (Material Transfer) (backport #34313) (backport #34336)
2023-03-07 22:29:01 +05:30
s-aga-r
6841e22ffe perf: update_completed_qty() in material_request.py
(cherry picked from commit 8ad9e99cea)
(cherry picked from commit 5bc2b8f685)
2023-03-07 16:07:23 +00:00
s-aga-r
56a422deed perf: Stock Entry (Material Transfer)
(cherry picked from commit de18f98c5c)
(cherry picked from commit 7a159a7187)
2023-03-07 16:07:22 +00:00
Frappe PR Bot
0ec34e5880 chore(release): Bumped to Version 13.49.2
## [13.49.2](https://github.com/frappe/erpnext/compare/v13.49.1...v13.49.2) (2023-03-07)

### Bug Fixes

* `rejected_serial_no` not getting copied from PR to PR(Return) ([bb55210](bb55210f49))
* `Serial No is mandatory` even if the `qty` is `0` ([9bea2fc](9bea2fcdfc))
* Default sales team not getting set ([#34284](https://github.com/frappe/erpnext/issues/34284)) ([65c0189](65c0189c4d))
* **minor:** Dirty the form after clicking on Get advances button in Invoices ([#34323](https://github.com/frappe/erpnext/issues/34323)) ([3a1475a](3a1475a90b))
* **test:** check for batch_no in returned dict ([8c5322c](8c5322c1cb))
* UI freeze while selecting batched items in sales invoice ([82a8f2b](82a8f2b1b2))
* Wrap unexpectedly long text in remark ([ba66a67](ba66a6714c))
2023-03-07 10:43:39 +00:00
Deepesh Garg
ba58c7ed59 Merge pull request #34326 from frappe/version-13-hotfix
chore: release v13
2023-03-07 16:11:57 +05:30
mergify[bot]
3a1475a90b fix(minor): Dirty the form after clicking on Get advances button in Invoices (#34323)
fix(minor): Dirty the form after clicking on Get advances button in Invoices (#34323)

fix(minor): Dirty form after clicking on Get advances button

(cherry picked from commit 2feb27e399)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2023-03-07 15:47:37 +05:30
mergify[bot]
65c0189c4d fix: Default sales team not getting set (#34284)
fix: Default sales team not getting set (#34284)

(cherry picked from commit 7d0199d743)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-03-07 13:33:55 +05:30
mergify[bot]
1b2c4bf868 chore: add german translations (#34167)
* chore: add german translations (#34167)

* chore: add german translations

* Apply suggestions from code review

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

---------

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

# Conflicts:
#	erpnext/translations/de.csv

* chore: resolve conflicts

---------

Co-authored-by: Patrick Eissler <77415730+PatrickDenis-stack@users.noreply.github.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-03-07 11:46:30 +05:30
ruthra kumar
88ed6e6cb4 Merge pull request #34304 from ruthra-kumar/ui_freeze_on_item_selection
fix: UI freeze while selecting batched items in sales invoice
2023-03-06 11:24:01 +05:30
ruthra kumar
8c5322c1cb fix(test): check for batch_no in returned dict 2023-03-05 20:47:31 +05:30
ruthra kumar
82a8f2b1b2 fix: UI freeze while selecting batched items in sales invoice 2023-03-05 20:47:29 +05:30
Sagar Sharma
3908b510bd Merge pull request #34276 from frappe/mergify/bp/version-13-hotfix/pr-34273
fix: `rejected_serial_no` not getting copied from PR to PR(Return) (backport #34273)
2023-03-04 15:35:00 +05:30
s-aga-r
14547d94b3 chore: conflicts 2023-03-04 15:04:50 +05:30
s-aga-r
9bea2fcdfc fix: Serial No is mandatory even if the qty is 0
(cherry picked from commit cb0b6de4b9)
2023-03-02 07:08:14 +00:00
s-aga-r
bb55210f49 fix: rejected_serial_no not getting copied from PR to PR(Return)
(cherry picked from commit a9f0a11ce6)

# Conflicts:
#	erpnext/controllers/sales_and_purchase_return.py
2023-03-02 07:08:14 +00:00
Frappe PR Bot
178be42369 chore(release): Bumped to Version 13.49.1
## [13.49.1](https://github.com/frappe/erpnext/compare/v13.49.0...v13.49.1) (2023-03-01)

### Bug Fixes

* Wrap unexpectedly long text in remark ([e694550](e6945508f1))
2023-03-01 10:55:31 +00:00
Suraj Shetty
b4e775b264 Merge pull request #34264 from frappe/mergify/bp/version-13/pr-34262
fix(General Ledger): Wrap unexpectedly long word  (backport #34262)
2023-03-01 16:23:56 +05:30
Suraj Shetty
e6945508f1 fix: Wrap unexpectedly long text in remark
(cherry picked from commit ba66a6714c)
2023-03-01 10:53:42 +00:00
Suraj Shetty
5354169f31 Merge pull request #34262 from frappe/fix-general-ledger-report 2023-03-01 16:22:28 +05:30
Suraj Shetty
ba66a6714c fix: Wrap unexpectedly long text in remark 2023-03-01 16:16:58 +05:30
Frappe PR Bot
573cd3c33b chore(release): Bumped to Version 13.49.0
# [13.49.0](https://github.com/frappe/erpnext/compare/v13.48.1...v13.49.0) (2023-02-28)

### Bug Fixes

* conversion factor not set ([59d5797](59d579764d))
* german translations ([#31732](https://github.com/frappe/erpnext/issues/31732)) ([d44da6c](d44da6c820))
* ignore remaining leaves calculation for cf leaves after expiry ([d82ba4e](d82ba4e86f))
* incorrect acc depr amount if multiple FBs with straight line or manual method ([304e6bb](304e6bb996))
* incorrect color in the BOM Stock Report ([e98b346](e98b34617f))
* incorrect leave balance after carry-forwarded leave expiry ([a3a9cd5](a3a9cd5174))
* manual depr schedule ([7176799](71767994a7))
* multiple pos conversion issue resolved ([de631e6](de631e65cc))
* not able to repost gl entries ([2039bd0](2039bd066d))
* **patch:** create only 80G custom fields instead of running the whole setup ([#34183](https://github.com/frappe/erpnext/issues/34183)) ([806f7e5](806f7e5eef))
* permission error while calling get_work_order_items ([3d7b2b1](3d7b2b1a6d))
* pos return throwing amount greater than grand total ([f6607a6](f6607a6050))
* Remove missing DocField in fetch_from ([45645c1](45645c1064))
* set `from_warehouse` and `to_warehouse` while mapping SE ([b1ecca3](b1ecca3a16))
* **test:** use standalone method to fetch work orders from SO ([7971c14](7971c149ed))
* ui freeze on item selection in sales invoice ([d1b611d](d1b611d37f))
* user shouldn't able to make item price for item template ([69f1247](69f1247fab))
* zero division error while making LCV ([91a95ad](91a95adcb6))

### Features

* provision to convert transaction based reposting to item warehouse based reposting ([59c6eb5](59c6eb591b))

### Performance Improvements

* fetch SLE's on demand and memoize ([642692a](642692a040))
2023-02-28 13:29:25 +00:00
ruthra kumar
b6edadb3cb Merge pull request #34239 from frappe/version-13-hotfix
chore: release v13
2023-02-28 18:57:46 +05:30
ruthra kumar
d65df443fc Merge pull request #34246 from frappe/mergify/bp/version-13-hotfix/pr-34241
fix: pos return throwing amount greater than grand total (backport #34241)
2023-02-28 18:33:47 +05:30
ruthra kumar
f6607a6050 fix: pos return throwing amount greater than grand total
(cherry picked from commit 35c70f39fa)
2023-02-28 12:53:30 +00:00
Sagar Sharma
75d98ef205 Merge pull request #34237 from frappe/mergify/bp/version-13-hotfix/pr-34060
fix: multiple Point of Sale conversion issue resolved (backport #34060)
2023-02-28 16:49:46 +05:30
Vishal
fd1d2cd203 chore: minor changes in pos_controller
(cherry picked from commit f18ae5856f)
2023-02-28 09:26:37 +00:00
Vishal
c66dc5658f chore: minor change
(cherry picked from commit a51bec0269)
2023-02-28 09:26:36 +00:00
Vishal
1ebf2dd2bf chore: minor changes added to code
(cherry picked from commit 3ebe7d861d)
2023-02-28 09:26:36 +00:00
Vishal
de631e65cc fix: multiple pos conversion issue resolved
(cherry picked from commit 1de531e56e)
2023-02-28 09:26:36 +00:00
ruthra kumar
02f2844db2 Merge pull request #34218 from frappe/mergify/bp/version-13-hotfix/pr-34207
fix: permission error while calling get_work_order_items (backport #34207)
2023-02-28 11:04:52 +05:30
Frappe PR Bot
c4d9576f9f chore(release): Bumped to Version 13.48.1
## [13.48.1](https://github.com/frappe/erpnext/compare/v13.48.0...v13.48.1) (2023-02-27)

### Bug Fixes

* not able to repost gl entries ([a34aff6](a34aff6f49))
2023-02-27 14:34:43 +00:00
rohitwaghchaure
74303b65cf Merge pull request #34228 from frappe/mergify/bp/version-13/pr-34209
fix: not able to repost gl entries (backport #34206) (backport #34209)
2023-02-27 20:03:15 +05:30
Rohit Waghchaure
a34aff6f49 fix: not able to repost gl entries
(cherry picked from commit 7d10dd9ea8)
(cherry picked from commit 2039bd066d)
2023-02-27 14:08:29 +00:00
Sagar Sharma
f105c1bd5e Merge pull request #34227 from frappe/mergify/bp/version-13-hotfix/pr-34225
fix: set `from_warehouse` and `to_warehouse` while mapping SE (backport #34225)
2023-02-27 17:56:08 +05:30
Sagar Sharma
264c314416 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-34225 2023-02-27 17:32:34 +05:30
Sagar Sharma
a71a336e59 chore: conflicts 2023-02-27 17:32:05 +05:30
Anand Baburajan
7db3645298 Merge pull request #34215 from frappe/mergify/bp/version-13-hotfix/pr-34205
fix: asset manual depr schedule (backport #34205)
2023-02-27 13:31:44 +05:30
s-aga-r
b1ecca3a16 fix: set from_warehouse and to_warehouse while mapping SE
(cherry picked from commit c09a61f360)

# Conflicts:
#	erpnext/stock/doctype/material_request/material_request.py
2023-02-27 07:23:20 +00:00
Anand Baburajan
cbfa188d3d Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-34205 2023-02-27 12:41:45 +05:30
Sagar Sharma
4f7344c278 Merge pull request #34224 from frappe/mergify/bp/version-13-hotfix/pr-34212
fix: Remove missing DocField in fetch_from (backport #34212)
2023-02-27 12:28:55 +05:30
Brian Pond
45645c1064 fix: Remove missing DocField in fetch_from
(cherry picked from commit 83f3e317e1)
2023-02-27 06:24:23 +00:00
mergify[bot]
d44da6c820 fix: german translations (#31732)
fix: german translations (#31732)

(cherry picked from commit 6b510546ae)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-02-27 11:50:12 +05:30
Sagar Sharma
95ea28f14d Merge pull request #34209 from frappe/mergify/bp/version-13-hotfix/pr-34206
fix: not able to repost gl entries (backport #34206)
2023-02-27 10:26:38 +05:30
ruthra kumar
7971c149ed fix(test): use standalone method to fetch work orders from SO
(cherry picked from commit a11d3327df)
2023-02-27 10:14:36 +05:30
ruthra kumar
3d7b2b1a6d fix: permission error while calling get_work_order_items
(cherry picked from commit b6bad728cd)
2023-02-27 10:14:31 +05:30
anandbaburajan
9942a9d40a chore: refactor long if conditions
(cherry picked from commit d56ca011fe)
2023-02-26 14:38:43 +00:00
anandbaburajan
9a607b9bd0 chore: should prepare schedule if not draft
(cherry picked from commit 75386e3653)
2023-02-26 14:38:42 +00:00
anandbaburajan
304e6bb996 fix: incorrect acc depr amount if multiple FBs with straight line or manual method
(cherry picked from commit dda6baea3e)
2023-02-26 14:38:42 +00:00
anandbaburajan
4a557b47d7 chore: handle change in opening_accumulated_depreciation properly
(cherry picked from commit b0d670a51d)
2023-02-26 14:38:42 +00:00
anandbaburajan
71767994a7 fix: manual depr schedule
(cherry picked from commit 971c0720e5)
2023-02-26 14:38:41 +00:00
Rohit Waghchaure
2039bd066d fix: not able to repost gl entries
(cherry picked from commit 7d10dd9ea8)
2023-02-24 15:41:22 +00:00
rohitwaghchaure
b599b93ae8 Merge pull request #34201 from frappe/mergify/bp/version-13-hotfix/pr-34199
fix: conversion factor not set (backport #34199)
2023-02-24 17:52:40 +05:30
Rohit Waghchaure
59d579764d fix: conversion factor not set
(cherry picked from commit 8e46aebc50)
2023-02-24 09:28:37 +00:00
Frappe PR Bot
5f25cea322 chore(release): Bumped to Version 13.48.0
# [13.48.0](https://github.com/frappe/erpnext/compare/v13.47.0...v13.48.0) (2023-02-24)

### Bug Fixes

* incorrect color in the BOM Stock Report ([0490e3b](0490e3bfe6))

### Features

* provision to convert transaction based reposting to item warehouse based reposting ([c8ec365](c8ec365594))
2023-02-24 09:08:54 +00:00
rohitwaghchaure
6a0c24e7b3 Merge pull request #34196 from frappe/mergify/bp/version-13/pr-34178
fix: incorrect color in the BOM Stock Report (backport #34173) (backport #34178)
2023-02-24 14:37:28 +05:30
rohitwaghchaure
8eb6053c97 Merge pull request #34198 from frappe/mergify/bp/version-13/pr-34197
feat: provision to convert transaction based reposting to item wareho… (backport #34115) (backport #34197)
2023-02-24 14:37:14 +05:30
Rohit Waghchaure
c8ec365594 feat: provision to convert transaction based reposting to item warehouse based reposting
(cherry picked from commit f1383b5ef9)
(cherry picked from commit 59c6eb591b)
2023-02-24 06:27:41 +00:00
rohitwaghchaure
0fbd29b16d Merge pull request #34197 from frappe/mergify/bp/version-13-hotfix/pr-34115
feat: provision to convert transaction based reposting to item wareho… (backport #34115)
2023-02-24 11:54:56 +05:30
Rucha Mahabal
806f7e5eef fix(patch): create only 80G custom fields instead of running the whole setup (#34183) 2023-02-24 11:32:46 +05:30
Rohit Waghchaure
59c6eb591b feat: provision to convert transaction based reposting to item warehouse based reposting
(cherry picked from commit f1383b5ef9)
2023-02-24 05:51:56 +00:00
Rohit Waghchaure
0490e3bfe6 fix: incorrect color in the BOM Stock Report
(cherry picked from commit a8f03ebf7f)
(cherry picked from commit e98b34617f)
2023-02-24 05:40:48 +00:00
rohitwaghchaure
0aeef34944 Merge pull request #34191 from frappe/mergify/bp/version-13-hotfix/pr-34189
fix: user shouldn't able to make item price for item template (backport #34189)
2023-02-24 09:23:41 +05:30
Rohit Waghchaure
69f1247fab fix: user shouldn't able to make item price for item template
(cherry picked from commit 6417ae0ee8)
2023-02-23 15:18:42 +00:00
rohitwaghchaure
2d01b72b04 Merge pull request #34178 from frappe/mergify/bp/version-13-hotfix/pr-34173
fix: incorrect color in the BOM Stock Report (backport #34173)
2023-02-23 20:47:07 +05:30
ruthra kumar
bb4c968d95 Merge pull request #34185 from frappe/mergify/bp/version-13-hotfix/pr-34022
perf: Gross Profit report will fetch SLE's on demand and memoize (backport #34022)
2023-02-23 12:54:47 +05:30
ruthra kumar
c40aa580c5 refactor: use docstatus from Delivery Note Item
(cherry picked from commit 88d888d9d0)
2023-02-23 06:26:15 +00:00
ruthra kumar
642692a040 perf: fetch SLE's on demand and memoize
(cherry picked from commit 3e5691072a)
2023-02-23 06:26:15 +00:00
rohitwaghchaure
fd24d52d86 Merge pull request #34182 from frappe/mergify/bp/version-13-hotfix/pr-34172
fix: zero division error while making LCV (backport #34172)
2023-02-23 11:25:24 +05:30
Rohit Waghchaure
91a95adcb6 fix: zero division error while making LCV
(cherry picked from commit 80e94a08cf)
2023-02-23 05:24:56 +00:00
ruthra kumar
fe04b5a2b9 Merge pull request #34179 from frappe/mergify/bp/version-13-hotfix/pr-34176
fix: ui freeze upon item selection in sales invoice (backport #34176)
2023-02-23 10:52:12 +05:30
ruthra kumar
d1b611d37f fix: ui freeze on item selection in sales invoice
(cherry picked from commit 6412583e98)
2023-02-23 05:06:50 +00:00
Rohit Waghchaure
e98b34617f fix: incorrect color in the BOM Stock Report
(cherry picked from commit a8f03ebf7f)
2023-02-23 04:25:45 +00:00
Rucha Mahabal
6391ccd56a Merge pull request #34175 from ruchamahabal/fix-leave-balances 2023-02-22 20:17:13 +05:30
Rucha Mahabal
b848b77815 test: leave details with expired cf leaves 2023-02-22 19:46:08 +05:30
Rucha Mahabal
d82ba4e86f fix: ignore remaining leaves calculation for cf leaves after expiry
- calculate correct cf expiry in the entire allocation period
2023-02-22 19:44:23 +05:30
Rucha Mahabal
aea9d82672 test: leaves allocated before and after cf leave expiry is same 2023-02-22 19:44:06 +05:30
Rucha Mahabal
a3a9cd5174 fix: incorrect leave balance after carry-forwarded leave expiry 2023-02-22 19:43:56 +05:30
Frappe PR Bot
9766827a08 chore(release): Bumped to Version 13.47.0
# [13.47.0](https://github.com/frappe/erpnext/compare/v13.46.1...v13.47.0) (2023-02-21)

### Bug Fixes

* add missing import ([8add12d](8add12d568))
* Amount for debit and credit notes with 0 qty line items (backport [#33902](https://github.com/frappe/erpnext/issues/33902)) ([#34123](https://github.com/frappe/erpnext/issues/34123)) ([2408966](2408966090))
* asset repair status after deletion and asset status after manual depr entry ([922b30a](922b30a566))
* asset_depreciation_and_balances report doesn't reflect manual depr entries ([6227c16](6227c16374))
* check for duplicate in pos closing and pos merge log entry ([92da1ed](92da1ed3c2))
* don't get chart data if data is empty ([acdf7fd](acdf7fd8df))
* **ecommerce:** throw invalid doctype error in shop by category ([#33901](https://github.com/frappe/erpnext/issues/33901)) ([de87786](de87786db4))
* Filters in item-wise sales history report ([#34145](https://github.com/frappe/erpnext/issues/34145)) ([9826245](9826245d8a))
* fiscal year error for existing assets in fixed asset register ([1fb3a28](1fb3a28128))
* opening_accumulated_depreciation and precision in charts ([4f10f48](4f10f48f7c))
* should never get cutomer price on purchase document ([#34002](https://github.com/frappe/erpnext/issues/34002)) ([117dbe3](117dbe38c4)), closes [#33998](https://github.com/frappe/erpnext/issues/33998)
* update `reserved_qty` when `Sales Order` marked as `Hold` ([2391c37](2391c37238))
* Use normal rounding for Tax Withholding Category ([#34114](https://github.com/frappe/erpnext/issues/34114)) ([26ed460](26ed460a4f))
* **ux:** `ReferenceError: me is not defined` Delivery Note ([495d1b2](495d1b2548))

### Features

* **UX:** Add option to disable consolidating leave types in balance reports ([ccd2568](ccd25684f9))
2023-02-21 17:21:05 +00:00
Deepesh Garg
eeaa8b2479 Merge pull request #34160 from frappe/version-13-hotfix
chore: release v13
2023-02-21 22:49:22 +05:30
ruthra kumar
c7093b6e96 Merge pull request #34165 from frappe/mergify/bp/version-13-hotfix/pr-34102
fix: check for duplicate pos invoices in closing entry (backport #34102)
2023-02-21 20:04:02 +05:30
ruthra kumar
92da1ed3c2 fix: check for duplicate in pos closing and pos merge log entry
(cherry picked from commit 47add0b751)
2023-02-21 13:16:32 +00:00
mergify[bot]
9826245d8a fix: Filters in item-wise sales history report (#34145)
fix: Filters in item-wise sales history report (#34145)

(cherry picked from commit c88444a6c4)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-21 15:06:21 +05:30
mergify[bot]
26ed460a4f fix: Use normal rounding for Tax Withholding Category (#34114)
fix: Use normal rounding for Tax Withholding Category (#34114)

(cherry picked from commit 35cdd996a9)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-21 14:23:16 +05:30
Anand Baburajan
7bed6cddc7 Merge pull request #34156 from frappe/mergify/bp/version-13-hotfix/pr-34153
fix: fiscal year error for existing assets in fixed asset register (backport #34153)
2023-02-21 14:19:45 +05:30
anandbaburajan
1fb3a28128 fix: fiscal year error for existing assets in fixed asset register
(cherry picked from commit 76861eb332)
2023-02-21 08:25:47 +00:00
Rucha Mahabal
c49c621e43 Merge pull request #34150 from ruchamahabal/consolidate-balance-entries 2023-02-21 13:01:32 +05:30
Rucha Mahabal
8add12d568 fix: add missing import 2023-02-21 12:33:47 +05:30
Rucha Mahabal
acdf7fd8df fix: don't get chart data if data is empty 2023-02-21 12:30:52 +05:30
Rucha Mahabal
ccd25684f9 feat(UX): Add option to disable consolidating leave types in balance reports 2023-02-21 12:30:24 +05:30
Sagar Sharma
e34f5c9cf7 Merge pull request #34147 from frappe/mergify/bp/version-13-hotfix/pr-34138
fix(ux): `ReferenceError: me is not defined` Delivery Note (backport #34138)
2023-02-21 10:31:27 +05:30
s-aga-r
bdefd700af chore: Linters
(cherry picked from commit 44ee9f0f19)
2023-02-21 04:57:40 +00:00
s-aga-r
495d1b2548 fix(ux): ReferenceError: me is not defined Delivery Note
(cherry picked from commit 1b010add26)
2023-02-21 04:57:40 +00:00
mergify[bot]
2408966090 fix: Amount for debit and credit notes with 0 qty line items (backport #33902) (#34123)
fix: Amount for debit and credit notes with 0 qty line items (#33902)

(cherry picked from commit 47c91324b1)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-19 11:37:44 +05:30
Anand Baburajan
58a006ff64 Merge pull request #34113 from frappe/mergify/bp/version-13-hotfix/pr-34112
fix: repair status after deletion, asset status after manual depr entry and other misc bugs [v14] (backport #34112)
2023-02-17 16:52:28 +05:30
anandbaburajan
3585b90ce5 chore: fixing conflict 2023-02-17 16:29:09 +05:30
anandbaburajan
922b30a566 fix: asset repair status after deletion and asset status after manual depr entry
(cherry picked from commit 03f07a20e7)

# Conflicts:
#	erpnext/assets/doctype/asset/asset.py
2023-02-17 10:44:04 +00:00
Deepesh Garg
5fc68a3dfe Merge pull request #34017 from frappe/mergify/bp/version-13-hotfix/pr-33901
fix(ecommerce): throw invalid doctype error in shop by category (backport #33901)
2023-02-15 16:59:18 +05:30
Frappe PR Bot
4a95c9d642 chore(release): Bumped to Version 13.46.1
## [13.46.1](https://github.com/frappe/erpnext/compare/v13.46.0...v13.46.1) (2023-02-15)

### Bug Fixes

* asset_depreciation_and_balances report doesn't reflect manual depr entries ([62dc68b](62dc68bb57))
* opening_accumulated_depreciation and precision in charts ([6308fca](6308fca587))
2023-02-15 10:33:52 +00:00
Anand Baburajan
f6707b2b92 Merge pull request #34075 from frappe/mergify/bp/version-13/pr-34073
fix: manual depr entries in asset_depreciations_and_balances report and some misc bugs [v14] (backport #34058) (backport #34073)
2023-02-15 16:02:25 +05:30
Sagar Sharma
f6d8adc921 Merge pull request #34068 from frappe/mergify/bp/version-13-hotfix/pr-34002
fix: should never get cutomer price on purchase document (backport #34002)
2023-02-15 16:01:17 +05:30
Sagar Sharma
48f2bd9add Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-34002 2023-02-15 15:22:02 +05:30
anandbaburajan
62dc68bb57 fix: asset_depreciation_and_balances report doesn't reflect manual depr entries
(cherry picked from commit 1535c3d856)
(cherry picked from commit 6227c16374)
2023-02-15 08:36:50 +00:00
anandbaburajan
f80fb97c71 chore: break look if je processed
(cherry picked from commit a220dc0c9c)
(cherry picked from commit ff2e617c0c)
2023-02-15 08:36:50 +00:00
anandbaburajan
6308fca587 fix: opening_accumulated_depreciation and precision in charts
(cherry picked from commit 47cc8ab6c6)
(cherry picked from commit 4f10f48f7c)
2023-02-15 08:36:50 +00:00
Anand Baburajan
fb3a411d1f Merge pull request #34073 from frappe/mergify/bp/version-13-hotfix/pr-34058
fix: manual depr entries in asset_depreciations_and_balances report and some misc bugs [v14] (backport #34058)
2023-02-15 13:43:36 +05:30
anandbaburajan
6227c16374 fix: asset_depreciation_and_balances report doesn't reflect manual depr entries
(cherry picked from commit 1535c3d856)
2023-02-15 07:04:18 +00:00
anandbaburajan
ff2e617c0c chore: break look if je processed
(cherry picked from commit a220dc0c9c)
2023-02-15 07:04:18 +00:00
anandbaburajan
4f10f48f7c fix: opening_accumulated_depreciation and precision in charts
(cherry picked from commit 47cc8ab6c6)
2023-02-15 07:04:17 +00:00
Anand Baburajan
612ceb59c7 Merge pull request #34063 from frappe/mergify/bp/version-13-hotfix/pr-34059
chore: add anand to asset's codeowner (backport #34059)
2023-02-15 11:44:41 +05:30
HENRY Florian
117dbe38c4 fix: should never get cutomer price on purchase document (#34002)
* fix: never get cutomer price on purchase document

chores: syntax

chore: typo in stock_entry get_uom_details (#33998)

fix: typo in stock_entry get_uom_details

chores: syntax

* feat: add test for get_item_detail price list oriented

* feat: add test for get_item_detail price price oriented

* feat: add test for get_item_detail price price oriented

* chore: clean test code

(cherry picked from commit 231fe4156f)
2023-02-15 02:38:50 +00:00
Anand Baburajan
1223e31e7d Update CODEOWNERS 2023-02-14 20:33:40 +05:30
anandbaburajan
bc59ea0d55 chore: add anand to asset's codeowner
(cherry picked from commit d003370f61)

# Conflicts:
#	CODEOWNERS
2023-02-14 14:22:03 +00:00
Sabu Siyad
6ae1cc020a resolve conflicts 2023-02-14 19:34:39 +05:30
Sabu Siyad
4278bfe7b3 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-33901 2023-02-14 19:05:14 +05:30
Sagar Sharma
574791f2c9 Merge pull request #34056 from frappe/mergify/bp/version-13-hotfix/pr-34018
fix: update `reserved_qty` when `Sales Order` marked as `Hold` (backport #34018)
2023-02-14 17:40:40 +05:30
s-aga-r
2391c37238 fix: update reserved_qty when Sales Order marked as Hold
(cherry picked from commit d76759e066)
2023-02-14 11:23:16 +00:00
Frappe PR Bot
ab71a7bba8 chore(release): Bumped to Version 13.46.0
# [13.46.0](https://github.com/frappe/erpnext/compare/v13.45.1...v13.46.0) (2023-02-14)

### Bug Fixes

* `amount` in `Material Request` ([813e8bb](813e8bb664))
* allow PI cancel if linked asset is cancelled ([fbeaabf](fbeaabffc9))
* conflicts ([a9f5be3](a9f5be3f98))
* currency formatting in item-wise sales history ([#33903](https://github.com/frappe/erpnext/issues/33903)) ([f641039](f6410393ce))
* Debit and Credit not equal while submitting PI containing asset item ([#33092](https://github.com/frappe/erpnext/issues/33092)) ([5be4c6f](5be4c6ffbc))
* german chart of accounts "SKR03" ([#33909](https://github.com/frappe/erpnext/issues/33909)) ([b2a3e01](b2a3e014e9))
* incorrect actual qty in Bin ([8f42833](8f42833fba))
* LWP calculation ([c1de4e4](c1de4e4420))
* negative stock error ([2f4ffe1](2f4ffe137e))
* stock entry from item dashboard (stock levels) ([8106c64](8106c64c91))
* **UX:** make Item attachments public by default (backport [#32196](https://github.com/frappe/erpnext/issues/32196)) ([#33949](https://github.com/frappe/erpnext/issues/33949)) ([124d7de](124d7dea1b))
* validate working day list against holidays ([a8ea3ef](a8ea3efae2))

### Features

* Setting to allow Sales Order creation against expired quotation ([#33952](https://github.com/frappe/erpnext/issues/33952)) ([f04542e](f04542eac9))
2023-02-14 10:40:00 +00:00
Deepesh Garg
958a3320e8 Merge pull request #34052 from frappe/version-13-hotfix
chore: release v13
2023-02-14 16:07:17 +05:30
Saurabh
60d2bf939b Merge pull request #34041 from saurabh6790/fix-lwp-calculations
fix: LWP calculation
2023-02-14 15:26:01 +05:30
Saurabh
a8ea3efae2 fix: validate working day list against holidays 2023-02-14 13:14:58 +05:30
Saurabh
c1de4e4420 fix: LWP calculation 2023-02-14 10:52:10 +05:30
mergify[bot]
f04542eac9 feat: Setting to allow Sales Order creation against expired quotation (#33952)
* feat: Setting to allow Sales Order creation against expired quotation (#33952)

* feat: Setting to allow Sales Order creation against expired quotation

* chore: linting issues

(cherry picked from commit 148703bfc2)

# Conflicts:
#	erpnext/selling/doctype/selling_settings/selling_settings.json

* chore: Resolve conflicts

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-13 11:39:30 +05:30
Sabu Siyad
de87786db4 fix(ecommerce): throw invalid doctype error in shop by category (#33901)
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
(cherry picked from commit 0df28c7174)

# Conflicts:
#	erpnext/www/shop-by-category/index.py
2023-02-12 06:58:27 +00:00
mergify[bot]
f6410393ce fix: currency formatting in item-wise sales history (#33903)
fix: currency formatting in item-wise sales history (#33903)

* fix(item-sales-history): currency formatting

* chore: linting issues

* fix: convert raw sql to qb

(cherry picked from commit 2cc7239dd5)

Co-authored-by: Dany Robert <danyrt@wahni.com>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-11 11:10:58 +05:30
mergify[bot]
5be4c6ffbc fix: Debit and Credit not equal while submitting PI containing asset item (#33092)
fix: Debit and Credit not equal while submitting PI containing asset item

(cherry picked from commit dc8d635120)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-11 09:02:06 +05:30
mergify[bot]
124d7dea1b fix(UX): make Item attachments public by default (backport #32196) (#33949)
* fix(UX): make Item attachments public by default (#32196)

(cherry picked from commit fffc245922)

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

* chore: conflicts

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-02-10 21:39:33 +05:30
mergify[bot]
bbcdd1e2e2 chore: typo in stock_entry get_uom_details (backport #33998) (#34004)
chore: typo in stock_entry get_uom_details (#33998)

fix: typo in stock_entry get_uom_details
(cherry picked from commit 185c543b73)

Co-authored-by: Akshay <60477442+akshayitzme@users.noreply.github.com>
2023-02-10 20:55:52 +05:30
Anand Baburajan
b6bc29ac92 Merge pull request #33963 from frappe/mergify/bp/version-13-hotfix/pr-33946
fix: allow cancelling purchase invoice if linked asset is already cancelled (backport #33946)
2023-02-10 14:15:44 +05:30
Anand Baburajan
e9a453c430 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-33946 2023-02-08 23:23:31 +05:30
rohitwaghchaure
7e1d5e3595 Merge pull request #33956 from frappe/mergify/bp/version-13-hotfix/pr-33936
fix: negative stock error (backport #33936)
2023-02-07 19:13:35 +05:30
rohitwaghchaure
a9f5be3f98 fix: conflicts 2023-02-07 13:46:40 +05:30
anandbaburajan
d6b0e622ea chore: use continue, not break
(cherry picked from commit 3380dc5dea)
2023-02-05 10:38:00 +00:00
anandbaburajan
fbeaabffc9 fix: allow PI cancel if linked asset is cancelled
(cherry picked from commit b961321de5)
2023-02-05 10:38:00 +00:00
Sagar Sharma
c7c611d929 Merge pull request #33959 from frappe/mergify/bp/version-13-hotfix/pr-33942
fix: stock entry from item dashboard (stock levels) (backport #33942)
2023-02-05 10:06:14 +05:30
s-aga-r
8106c64c91 fix: stock entry from item dashboard (stock levels)
(cherry picked from commit dc0ddf8d7e)
2023-02-05 04:17:35 +00:00
Rohit Waghchaure
bd1191783b test: test case
(cherry picked from commit 9ae7578b07)
2023-02-04 18:06:07 +00:00
Rohit Waghchaure
2f4ffe137e fix: negative stock error
(cherry picked from commit 6d513e2519)

# Conflicts:
#	erpnext/stock/stock_ledger.py
2023-02-04 18:06:07 +00:00
Sagar Sharma
6735b09dd9 Merge pull request #33951 from frappe/mergify/bp/version-13-hotfix/pr-33940
chore: report `Warehouse wise Item Balance Age and Value` (backport #33940)
2023-02-04 18:58:46 +05:30
s-aga-r
745bef8ebc chore: conflicts 2023-02-04 13:51:17 +05:30
s-aga-r
5a9673ae1f chore: add Item Name column in Warehouse wise Item Balance Age and Value report
(cherry picked from commit 56356ffbb9)
2023-02-04 07:59:04 +00:00
s-aga-r
8d0b45b835 chore: column width in Warehouse wise Item Balance Age and Value report
(cherry picked from commit d7a665cb84)

# Conflicts:
#	erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py
2023-02-04 07:59:04 +00:00
Frappe PR Bot
6a9660de65 chore(release): Bumped to Version 13.45.1
## [13.45.1](https://github.com/frappe/erpnext/compare/v13.45.0...v13.45.1) (2023-02-01)

### Bug Fixes

* incorrect actual qty in Bin ([e3ad0b1](e3ad0b1655))
2023-02-01 17:46:11 +00:00
rohitwaghchaure
edbbb2469f Merge pull request #33928 from frappe/mergify/bp/version-13/pr-33922
fix: incorrect actual qty in Bin (backport #33918) (backport #33922)
2023-02-01 23:14:23 +05:30
Rohit Waghchaure
e3ad0b1655 fix: incorrect actual qty in Bin
(cherry picked from commit f8c852c54c)
(cherry picked from commit 8f42833fba)
2023-02-01 16:51:50 +00:00
rohitwaghchaure
7738ca1ce0 Merge pull request #33922 from frappe/mergify/bp/version-13-hotfix/pr-33918
fix: incorrect actual qty in Bin (backport #33918)
2023-02-01 22:20:31 +05:30
Rohit Waghchaure
8f42833fba fix: incorrect actual qty in Bin
(cherry picked from commit f8c852c54c)
2023-02-01 12:54:28 +00:00
mergify[bot]
b2a3e014e9 fix: german chart of accounts "SKR03" (#33909)
fix: german chart of accounts "SKR03" (#33909)

* fix: german chart of accounts "SKR03"

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

* style: convert indentation to tabs

* fix: space before percentage sign

* feat: add some expense accounts

* refactor: replace unicode characters with utf-8

for better readability

* revert: add back groups for Bank and Cash accounts

Removed in 7d0d9c6900

(cherry picked from commit 3c7b460fd8)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-02-01 14:17:59 +05:30
Sagar Sharma
64018c29f3 Merge pull request #33898 from frappe/mergify/bp/version-13-hotfix/pr-33869
fix: `amount` in `Material Request` (backport #33869)
2023-01-31 16:01:49 +05:30
s-aga-r
813e8bb664 fix: amount in Material Request
(cherry picked from commit 6b781d78e0)
2023-01-31 09:15:48 +00:00
72 changed files with 1532 additions and 764 deletions

View File

@@ -4,7 +4,7 @@
# the repo. Unless a later match takes precedence,
erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/assets/ @nextchamp-saqib @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

View File

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

View File

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

View File

@@ -248,21 +248,16 @@ class JournalEntry(AccountsController):
):
processed_assets.append(d.reference_name)
asset = frappe.db.get_value(
"Asset", d.reference_name, ["calculate_depreciation", "value_after_depreciation"], as_dict=1
)
asset = frappe.get_doc("Asset", d.reference_name)
if asset.calculate_depreciation:
continue
depr_value = d.debit or d.credit
frappe.db.set_value(
"Asset",
d.reference_name,
"value_after_depreciation",
asset.value_after_depreciation - depr_value,
)
asset.db_set("value_after_depreciation", asset.value_after_depreciation - depr_value)
asset.set_status()
def update_inter_company_jv(self):
if (
@@ -346,15 +341,14 @@ class JournalEntry(AccountsController):
finance_books.db_update()
asset.set_status()
break
else:
depr_value = d.debit or d.credit
frappe.db.set_value(
"Asset",
d.reference_name,
"value_after_depreciation",
asset.value_after_depreciation + depr_value,
)
asset.db_set("value_after_depreciation", asset.value_after_depreciation + depr_value)
asset.set_status()
def unlink_inter_company_jv(self):
if (

View File

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

View File

@@ -161,7 +161,7 @@ class POSInvoice(SalesInvoice):
bold_item_name = frappe.bold(item.item_name)
bold_extra_batch_qty_needed = frappe.bold(
abs(available_batch_qty - reserved_batch_qty - item.qty)
abs(available_batch_qty - reserved_batch_qty - item.stock_qty)
)
bold_invalid_batch_no = frappe.bold(item.batch_no)
@@ -172,7 +172,7 @@ class POSInvoice(SalesInvoice):
).format(item.idx, bold_invalid_batch_no, bold_item_name),
title=_("Item Unavailable"),
)
elif (available_batch_qty - reserved_batch_qty - item.qty) < 0:
elif (available_batch_qty - reserved_batch_qty - item.stock_qty) < 0:
frappe.throw(
_(
"Row #{}: Batch No. {} of item {} has less than required stock available, {} more required"
@@ -246,7 +246,7 @@ class POSInvoice(SalesInvoice):
),
title=_("Item Unavailable"),
)
elif is_stock_item and flt(available_stock) < flt(d.qty):
elif is_stock_item and flt(available_stock) < flt(d.stock_qty):
frappe.throw(
_(
"Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}."
@@ -652,7 +652,7 @@ def get_bundle_availability(bundle_item_code, warehouse):
item_pos_reserved_qty = get_pos_reserved_qty(item.item_code, warehouse)
available_qty = item_bin_qty - item_pos_reserved_qty
max_available_bundles = available_qty / item.qty
max_available_bundles = available_qty / item.stock_qty
if bundle_bin_qty > max_available_bundles and frappe.get_value(
"Item", item.item_code, "is_stock_item"
):

View File

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

View File

@@ -598,7 +598,7 @@ class PurchaseInvoice(BuyingController):
def make_supplier_gl_entry(self, gl_entries):
# Checked both rounding_adjustment and rounded_total
# because rounded_total had value even before introcution of posting GLE based on rounded total
# because rounded_total had value even before introduction of posting GLE based on rounded total
grand_total = (
self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
)
@@ -799,10 +799,7 @@ class PurchaseInvoice(BuyingController):
else item.deferred_expense_account
)
if not item.is_fixed_asset:
dummy, amount = self.get_amount_and_base_amount(item, None)
else:
amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount"))
dummy, amount = self.get_amount_and_base_amount(item, None)
if provisional_accounting_for_non_stock_items:
if item.purchase_receipt:

View File

@@ -1078,7 +1078,7 @@ var select_loyalty_program = function(frm, loyalty_programs) {
]
});
dialog.set_primary_action(__("Set"), function() {
dialog.set_primary_action(__("Set Loyalty Program"), function() {
dialog.hide();
return frappe.call({
method: "frappe.client.set_value",

View File

@@ -256,7 +256,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
tax_amount = get_tcs_amount(parties, inv, tax_details, vouchers, advance_vouchers)
if cint(tax_details.round_off_tax_amount):
tax_amount = round(tax_amount)
tax_amount = normal_round(tax_amount)
return tax_amount, tax_deducted, tax_deducted_on_advances, voucher_wise_amount
@@ -555,3 +555,20 @@ def is_valid_certificate(
valid = True
return valid
def normal_round(number):
"""
Rounds a number to the nearest integer.
:param number: The number to round.
"""
decimal_part = number - int(number)
if decimal_part >= 0.5:
decimal_part = 1
else:
decimal_part = 0
number = int(number) + decimal_part
return number

View File

@@ -135,6 +135,34 @@ def get_assets(filters):
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
0
end), 0) as accumulated_depreciation_as_on_from_date,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s and gle.posting_date <= a.disposal_date then
gle.debit
else
0
end), 0) as depreciation_eliminated_during_the_period,
ifnull(sum(case when gle.posting_date >= %(from_date)s and gle.posting_date <= %(to_date)s
and (ifnull(a.disposal_date, 0) = 0 or gle.posting_date <= a.disposal_date) then
gle.debit
else
0
end), 0) as depreciation_amount_during_the_period
from `tabGL Entry` gle
join `tabAsset` a on
gle.against_voucher = a.name
join `tabAsset Category Account` aca on
aca.parent = a.asset_category and aca.company_name = %(company)s
join `tabCompany` company on
company.name = %(company)s
where a.docstatus=1 and a.company=%(company)s and a.calculate_depreciation=0 and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
group by a.asset_category
union
SELECT a.asset_category,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then
0

View File

@@ -38,8 +38,11 @@
{% if(data[i].posting_date) { %}
<td>{%= frappe.datetime.str_to_user(data[i].posting_date) %}</td>
<td>{%= data[i].voucher_type %}
<br>{%= data[i].voucher_no %}</td>
<td>
<br>{%= data[i].voucher_no %}
</td>
{% var longest_word = cstr(data[i].remarks).split(" ").reduce((longest, word) => word.length > longest.length ? word : longest, ""); %}
<td {% if longest_word.length > 45 %} class="overflow-wrap-anywhere" {% endif %}>
<span>
{% if(!(filters.party || filters.account)) { %}
{%= data[i].party || data[i].account %}
<br>
@@ -49,11 +52,14 @@
{% if(data[i].bill_no) { %}
<br>{%= __("Supplier Invoice No") %}: {%= data[i].bill_no %}
{% } %}
</td>
<td style="text-align: right">
{%= format_currency(data[i].debit, filters.presentation_currency) %}</td>
<td style="text-align: right">
{%= format_currency(data[i].credit, filters.presentation_currency) %}</td>
</span>
</td>
<td style="text-align: right">
{%= format_currency(data[i].debit, filters.presentation_currency) %}
</td>
<td style="text-align: right">
{%= format_currency(data[i].credit, filters.presentation_currency) %}
</td>
{% } else { %}
<td></td>
<td></td>

View File

@@ -364,6 +364,7 @@ def get_column_names():
class GrossProfitGenerator(object):
def __init__(self, filters=None):
self.sle = {}
self.data = []
self.average_buying_rate = {}
self.filters = frappe._dict(filters)
@@ -373,7 +374,6 @@ class GrossProfitGenerator(object):
if filters.group_by == "Invoice":
self.group_items_by_invoice()
self.load_stock_ledger_entries()
self.load_product_bundle()
self.load_non_stock_items()
self.get_returned_invoice_items()
@@ -563,7 +563,7 @@ class GrossProfitGenerator(object):
return flt(row.qty) * item_rate
else:
my_sle = self.sle.get((item_code, row.warehouse))
my_sle = self.get_stock_ledger_entries(item_code, row.warehouse)
if (row.update_stock or row.dn_detail) and my_sle:
parenttype, parent = row.parenttype, row.parent
if row.dn_detail:
@@ -581,7 +581,7 @@ class GrossProfitGenerator(object):
dn["item_row"],
dn["warehouse"],
)
my_sle = self.sle.get((item_code, warehouse))
my_sle = self.get_stock_ledger_entries(item_code, row.warehouse)
return self.calculate_buying_amount_from_sle(
row, my_sle, parenttype, parent, item_row, item_code
)
@@ -597,15 +597,12 @@ class GrossProfitGenerator(object):
def get_buying_amount_from_so_dn(self, sales_order, so_detail, item_code):
from frappe.query_builder.functions import Sum
delivery_note = frappe.qb.DocType("Delivery Note")
delivery_note_item = frappe.qb.DocType("Delivery Note Item")
query = (
frappe.qb.from_(delivery_note)
.inner_join(delivery_note_item)
.on(delivery_note.name == delivery_note_item.parent)
frappe.qb.from_(delivery_note_item)
.select(Sum(delivery_note_item.incoming_rate * delivery_note_item.stock_qty))
.where(delivery_note.docstatus == 1)
.where(delivery_note_item.docstatus == 1)
.where(delivery_note_item.item_code == item_code)
.where(delivery_note_item.against_sales_order == sales_order)
.where(delivery_note_item.so_detail == so_detail)
@@ -840,24 +837,36 @@ class GrossProfitGenerator(object):
"Item", item_code, ["item_name", "description", "item_group", "brand"]
)
def load_stock_ledger_entries(self):
res = frappe.db.sql(
"""select item_code, voucher_type, voucher_no,
voucher_detail_no, stock_value, warehouse, actual_qty as qty
from `tabStock Ledger Entry`
where company=%(company)s and is_cancelled = 0
order by
item_code desc, warehouse desc, posting_date desc,
posting_time desc, creation desc""",
self.filters,
as_dict=True,
)
self.sle = {}
for r in res:
if (r.item_code, r.warehouse) not in self.sle:
self.sle[(r.item_code, r.warehouse)] = []
def get_stock_ledger_entries(self, item_code, warehouse):
if item_code and warehouse:
if (item_code, warehouse) not in self.sle:
sle = qb.DocType("Stock Ledger Entry")
res = (
qb.from_(sle)
.select(
sle.item_code,
sle.voucher_type,
sle.voucher_no,
sle.voucher_detail_no,
sle.stock_value,
sle.warehouse,
sle.actual_qty.as_("qty"),
)
.where(
(sle.company == self.filters.company)
& (sle.item_code == item_code)
& (sle.warehouse == warehouse)
& (sle.is_cancelled == 0)
)
.orderby(sle.item_code)
.orderby(sle.warehouse, sle.posting_date, sle.posting_time, sle.creation, order=Order.desc)
.run(as_dict=True)
)
self.sle[(r.item_code, r.warehouse)].append(r)
self.sle[(item_code, warehouse)] = res
return self.sle[(item_code, warehouse)]
return []
def load_product_bundle(self):
self.product_bundles = {}

View File

@@ -206,52 +206,53 @@ frappe.ui.form.on('Asset', {
return
}
var x_intervals = [frm.doc.purchase_date];
var x_intervals = [frappe.format(frm.doc.purchase_date, { fieldtype: 'Date' })];
var asset_values = [frm.doc.gross_purchase_amount];
var last_depreciation_date = frm.doc.purchase_date;
if(frm.doc.opening_accumulated_depreciation) {
last_depreciation_date = frappe.datetime.add_months(frm.doc.next_depreciation_date,
-1*frm.doc.frequency_of_depreciation);
x_intervals.push(last_depreciation_date);
asset_values.push(flt(frm.doc.gross_purchase_amount) -
flt(frm.doc.opening_accumulated_depreciation));
}
if(frm.doc.calculate_depreciation) {
if(frm.doc.opening_accumulated_depreciation) {
var depreciation_date = frappe.datetime.add_months(
frm.doc.finance_books[0].depreciation_start_date,
-1 * frm.doc.finance_books[0].frequency_of_depreciation
);
x_intervals.push(frappe.format(depreciation_date, { fieldtype: 'Date' }));
asset_values.push(flt(frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, precision('gross_purchase_amount')));
}
$.each(frm.doc.schedules || [], function(i, v) {
x_intervals.push(v.schedule_date);
var asset_value = flt(frm.doc.gross_purchase_amount) - flt(v.accumulated_depreciation_amount);
x_intervals.push(frappe.format(v.schedule_date, { fieldtype: 'Date' }));
var asset_value = flt(frm.doc.gross_purchase_amount - v.accumulated_depreciation_amount, precision('gross_purchase_amount'));
if(v.journal_entry) {
last_depreciation_date = v.schedule_date;
asset_values.push(asset_value);
} else {
if (in_list(["Scrapped", "Sold"], frm.doc.status)) {
asset_values.push(null);
} else {
asset_values.push(asset_value)
asset_values.push(asset_value);
}
}
});
} else {
if(frm.doc.opening_accumulated_depreciation) {
x_intervals.push(frappe.format(frm.doc.creation.split(" ")[0], { fieldtype: 'Date' }));
asset_values.push(flt(frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, precision('gross_purchase_amount')));
}
let depr_entries = (await frappe.call({
method: "get_manual_depreciation_entries",
doc: frm.doc,
})).message;
$.each(depr_entries || [], function(i, v) {
x_intervals.push(v.posting_date);
last_depreciation_date = v.posting_date;
x_intervals.push(frappe.format(v.posting_date, { fieldtype: 'Date' }));
let last_asset_value = asset_values[asset_values.length - 1]
asset_values.push(last_asset_value - v.value);
asset_values.push(flt(last_asset_value - v.value, precision('gross_purchase_amount')));
});
}
if(in_list(["Scrapped", "Sold"], frm.doc.status)) {
x_intervals.push(frm.doc.disposal_date);
x_intervals.push(frappe.format(frm.doc.disposal_date, { fieldtype: 'Date' }));
asset_values.push(0);
last_depreciation_date = frm.doc.disposal_date;
}
frm.dashboard.render_graph({
@@ -295,10 +296,6 @@ frappe.ui.form.on('Asset', {
// frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation));
},
opening_accumulated_depreciation: function(frm) {
erpnext.asset.set_accumulated_depreciation(frm);
},
make_schedules_editable: function(frm) {
if (frm.doc.finance_books) {
var is_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0
@@ -518,19 +515,23 @@ frappe.ui.form.on('Depreciation Schedule', {
},
depreciation_amount: function(frm, cdt, cdn) {
erpnext.asset.set_accumulated_depreciation(frm);
erpnext.asset.set_accumulated_depreciation(frm, locals[cdt][cdn].finance_book_id);
}
})
});
erpnext.asset.set_accumulated_depreciation = function(frm) {
if(frm.doc.depreciation_method != "Manual") return;
erpnext.asset.set_accumulated_depreciation = function(frm, finance_book_id) {
var depreciation_method = frm.doc.finance_books[Number(finance_book_id) - 1].depreciation_method;
if(depreciation_method != "Manual") return;
var accumulated_depreciation = flt(frm.doc.opening_accumulated_depreciation);
$.each(frm.doc.schedules || [], function(i, row) {
accumulated_depreciation += flt(row.depreciation_amount);
frappe.model.set_value(row.doctype, row.name,
"accumulated_depreciation_amount", accumulated_depreciation);
if (row.finance_book_id === finance_book_id) {
accumulated_depreciation += flt(row.depreciation_amount);
frappe.model.set_value(row.doctype, row.name, "accumulated_depreciation_amount", accumulated_depreciation);
};
})
};

View File

@@ -84,14 +84,55 @@ class Asset(AccountsController):
if self.calculate_depreciation:
self.value_after_depreciation = 0
self.set_depreciation_rate()
self.make_depreciation_schedule(date_of_disposal)
self.set_accumulated_depreciation(date_of_disposal, date_of_return)
if self.should_prepare_depreciation_schedule():
self.make_depreciation_schedule(date_of_disposal)
self.set_accumulated_depreciation(date_of_disposal, date_of_return)
else:
self.finance_books = []
self.value_after_depreciation = flt(self.gross_purchase_amount) - flt(
self.opening_accumulated_depreciation
)
def should_prepare_depreciation_schedule(self):
if not self.get("schedules"):
return True
old_asset_doc = self.get_doc_before_save()
if not old_asset_doc:
return True
have_asset_details_been_modified = (
old_asset_doc.gross_purchase_amount != self.gross_purchase_amount
or old_asset_doc.opening_accumulated_depreciation != self.opening_accumulated_depreciation
or old_asset_doc.number_of_depreciations_booked != self.number_of_depreciations_booked
)
if have_asset_details_been_modified:
return True
manual_fb_idx = -1
for d in self.finance_books:
if d.depreciation_method == "Manual":
manual_fb_idx = d.idx - 1
no_manual_depr_or_have_manual_depr_details_been_modified = (
manual_fb_idx == -1
or old_asset_doc.finance_books[manual_fb_idx].total_number_of_depreciations
!= self.finance_books[manual_fb_idx].total_number_of_depreciations
or old_asset_doc.finance_books[manual_fb_idx].frequency_of_depreciation
!= self.finance_books[manual_fb_idx].frequency_of_depreciation
or old_asset_doc.finance_books[manual_fb_idx].depreciation_start_date
!= getdate(self.finance_books[manual_fb_idx].depreciation_start_date)
or old_asset_doc.finance_books[manual_fb_idx].expected_value_after_useful_life
!= self.finance_books[manual_fb_idx].expected_value_after_useful_life
)
if no_manual_depr_or_have_manual_depr_details_been_modified:
return True
return False
def validate_item(self):
item = frappe.get_cached_value(
"Item", self.item_code, ["is_fixed_asset", "is_stock_item", "disabled"], as_dict=1
@@ -225,9 +266,7 @@ class Asset(AccountsController):
)
def make_depreciation_schedule(self, date_of_disposal):
if "Manual" not in [d.depreciation_method for d in self.finance_books] and not self.get(
"schedules"
):
if not self.get("schedules"):
self.schedules = []
if not self.available_for_use_date:
@@ -545,9 +584,7 @@ class Asset(AccountsController):
def set_accumulated_depreciation(
self, date_of_disposal=None, date_of_return=None, ignore_booked_entry=False
):
straight_line_idx = [
d.idx for d in self.get("schedules") if d.depreciation_method == "Straight Line"
]
straight_line_idx = []
finance_books = []
for i, d in enumerate(self.get("schedules")):
@@ -555,6 +592,12 @@ class Asset(AccountsController):
continue
if int(d.finance_book_id) not in finance_books:
straight_line_idx = [
s.idx
for s in self.get("schedules")
if s.finance_book_id == d.finance_book_id
and (s.depreciation_method == "Straight Line" or s.depreciation_method == "Manual")
]
accumulated_depreciation = flt(self.opening_accumulated_depreciation)
value_after_depreciation = flt(
self.get("finance_books")[cint(d.finance_book_id) - 1].value_after_depreciation
@@ -667,11 +710,15 @@ class Asset(AccountsController):
if self.journal_entry_for_scrap:
status = "Scrapped"
elif self.finance_books:
idx = self.get_default_finance_book_idx() or 0
else:
expected_value_after_useful_life = 0
value_after_depreciation = self.value_after_depreciation
expected_value_after_useful_life = self.finance_books[idx].expected_value_after_useful_life
value_after_depreciation = self.finance_books[idx].value_after_depreciation
if self.calculate_depreciation:
idx = self.get_default_finance_book_idx() or 0
expected_value_after_useful_life = self.finance_books[idx].expected_value_after_useful_life
value_after_depreciation = self.finance_books[idx].value_after_depreciation
if flt(value_after_depreciation) <= expected_value_after_useful_life:
status = "Fully Depreciated"
@@ -683,14 +730,16 @@ class Asset(AccountsController):
def get_value_after_depreciation(self, finance_book=None):
if not self.calculate_depreciation:
return self.value_after_depreciation
return flt(self.value_after_depreciation, self.precision("gross_purchase_amount"))
if not finance_book:
return self.get("finance_books")[0].value_after_depreciation
return flt(
self.get("finance_books")[0].value_after_depreciation, self.precision("gross_purchase_amount")
)
for row in self.get("finance_books"):
if finance_book == row.finance_book:
return row.value_after_depreciation
return flt(row.value_after_depreciation, self.precision("gross_purchase_amount"))
def get_default_finance_book_idx(self):
if not self.get("default_finance_book") and self.company:
@@ -701,24 +750,6 @@ class Asset(AccountsController):
if d.finance_book == self.default_finance_book:
return cint(d.idx) - 1
@frappe.whitelist()
def get_manual_depreciation_entries(self):
(_, _, depreciation_expense_account) = get_depreciation_accounts(self)
gle = frappe.qb.DocType("GL Entry")
records = (
frappe.qb.from_(gle)
.select(gle.voucher_no.as_("name"), gle.debit.as_("value"), gle.posting_date)
.where(gle.against_voucher == self.name)
.where(gle.account == depreciation_expense_account)
.where(gle.debit != 0)
.where(gle.is_cancelled == 0)
.orderby(gle.posting_date)
).run(as_dict=True)
return records
def validate_make_gl_entry(self):
purchase_document = self.get_purchase_document()
if not purchase_document:
@@ -835,6 +866,25 @@ class Asset(AccountsController):
make_gl_entries(gl_entries)
self.db_set("booked_fixed_asset", 1)
@frappe.whitelist()
def get_manual_depreciation_entries(self):
(_, _, depreciation_expense_account) = get_depreciation_accounts(self)
gle = frappe.qb.DocType("GL Entry")
records = (
frappe.qb.from_(gle)
.select(gle.voucher_no.as_("name"), gle.debit.as_("value"), gle.posting_date)
.where(gle.against_voucher == self.name)
.where(gle.account == depreciation_expense_account)
.where(gle.debit != 0)
.where(gle.is_cancelled == 0)
.orderby(gle.posting_date)
.orderby(gle.creation)
).run(as_dict=True)
return records
@frappe.whitelist()
def get_depreciation_rate(self, args, on_validate=False):
if isinstance(args, string_types):

View File

@@ -144,7 +144,7 @@ def make_depreciation_entry(asset_name, date=None):
finance_books.value_after_depreciation -= d.depreciation_amount
finance_books.db_update()
frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Successful")
asset.db_set("depr_entry_posting_status", "Successful")
asset.set_status()

View File

@@ -82,6 +82,9 @@ class AssetRepair(AccountsController):
self.asset_doc.prepare_depreciation_data()
self.asset_doc.save()
def after_delete(self):
frappe.get_doc("Asset", self.asset).set_status()
def check_repair_status(self):
if self.repair_status == "Pending":
frappe.throw(_("Please update Repair Status."))

View File

@@ -5,7 +5,7 @@
import frappe
from frappe import _
from frappe.query_builder.functions import Sum
from frappe.utils import cstr, formatdate, getdate
from frappe.utils import cstr, flt, formatdate, getdate
from erpnext.accounts.report.financial_statements import (
get_fiscal_year_data,
@@ -102,13 +102,9 @@ def get_data(filters):
]
assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
finance_book_filter = ("is", "not set")
if filters.finance_book:
finance_book_filter = ("=", filters.finance_book)
assets_linked_to_fb = frappe.db.get_all(
doctype="Asset Finance Book",
filters={"finance_book": finance_book_filter},
filters={"finance_book": filters.finance_book or ("is", "not set")},
pluck="parent",
)
@@ -155,6 +151,7 @@ def prepare_chart_data(data, filters):
filters.filter_based_on,
"Monthly",
company=filters.company,
ignore_fiscal_year=True,
)
for d in period_list:
@@ -194,7 +191,7 @@ def get_depreciation_amount_of_asset(asset, depreciation_amount_map, filters):
else:
depr_amount = get_manual_depreciation_amount_of_asset(asset, filters)
return depr_amount
return flt(depr_amount, 2)
def get_finance_book_value_map(filters):

View File

@@ -755,6 +755,8 @@ class BuyingController(StockController, Subcontracting):
asset.purchase_date = self.posting_date
asset.supplier = self.supplier
elif self.docstatus == 2:
if asset.docstatus == 2:
continue
if asset.docstatus == 0:
asset.set(field, None)
asset.supplier = None

View File

@@ -131,7 +131,7 @@ def validate_returned_items(doc):
)
elif ref.serial_no:
if not d.serial_no:
if d.qty and not d.serial_no:
frappe.throw(_("Row # {0}: Serial No is mandatory").format(d.idx))
else:
serial_nos = get_serial_nos(d.serial_no)
@@ -393,6 +393,16 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None):
if serial_nos:
target_doc.serial_no = "\n".join(serial_nos)
if source_doc.get("rejected_serial_no"):
returned_serial_nos = get_returned_serial_nos(
source_doc, source_parent, serial_no_field="rejected_serial_no"
)
rejected_serial_nos = list(
set(get_serial_nos(source_doc.rejected_serial_no)) - set(returned_serial_nos)
)
if rejected_serial_nos:
target_doc.rejected_serial_no = "\n".join(rejected_serial_nos)
if doctype == "Purchase Receipt":
returned_qty_map = get_returned_qty_map_for_row(
source_parent.name, source_parent.supplier, source_doc.name, doctype
@@ -587,7 +597,7 @@ def get_filters(
return filters
def get_returned_serial_nos(child_doc, parent_doc):
def get_returned_serial_nos(child_doc, parent_doc, serial_no_field="serial_no"):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
return_ref_field = frappe.scrub(child_doc.doctype)
@@ -596,7 +606,7 @@ def get_returned_serial_nos(child_doc, parent_doc):
serial_nos = []
fields = ["`{0}`.`serial_no`".format("tab" + child_doc.doctype)]
fields = [f"`{'tab' + child_doc.doctype}`.`{serial_no_field}`"]
filters = [
[parent_doc.doctype, "return_against", "=", parent_doc.name],
@@ -606,6 +616,6 @@ def get_returned_serial_nos(child_doc, parent_doc):
]
for row in frappe.get_all(parent_doc.doctype, fields=fields, filters=filters):
serial_nos.extend(get_serial_nos(row.serial_no))
serial_nos.extend(get_serial_nos(row.get(serial_no_field)))
return serial_nos

View File

@@ -86,6 +86,9 @@ class SellingController(StockController):
)
if not self.meta.get_field("sales_team"):
party_details.pop("sales_team")
else:
self.set("sales_team", party_details.get("sales_team"))
self.update_if_missing(party_details)
elif lead:

View File

@@ -345,7 +345,8 @@
"image_field": "website_image",
"index_web_pages_for_search": 1,
"links": [],
"modified": "2022-06-28 17:10:30.613251",
"make_attachments_public": 1,
"modified": "2022-09-13 04:05:11.614087",
"modified_by": "Administrator",
"module": "E-commerce",
"name": "Website Item",

View File

@@ -817,7 +817,9 @@ def get_leave_balance_on(
allocation = allocation_records.get(leave_type, frappe._dict())
end_date = allocation.to_date if cint(consider_all_leaves_in_the_allocation_period) else date
cf_expiry = get_allocation_expiry_for_cf_leaves(employee, leave_type, to_date, date)
cf_expiry = get_allocation_expiry_for_cf_leaves(
employee, leave_type, to_date, allocation.from_date
)
leaves_taken = get_leaves_for_period(employee, leave_type, allocation.from_date, end_date)
@@ -832,6 +834,7 @@ def get_leave_balance_on(
def get_leave_allocation_records(employee, date, leave_type=None):
"""Returns the total allocated leaves and carry forwarded leaves based on ledger entries"""
Ledger = frappe.qb.DocType("Leave Ledger Entry")
LeaveAllocation = frappe.qb.DocType("Leave Allocation")
cf_leave_case = (
frappe.qb.terms.Case().when(Ledger.is_carry_forward == "1", Ledger.leaves).else_(0)
@@ -845,6 +848,8 @@ def get_leave_allocation_records(employee, date, leave_type=None):
query = (
frappe.qb.from_(Ledger)
.inner_join(LeaveAllocation)
.on(Ledger.transaction_name == LeaveAllocation.name)
.select(
sum_cf_leaves,
sum_new_leaves,
@@ -854,12 +859,21 @@ def get_leave_allocation_records(employee, date, leave_type=None):
)
.where(
(Ledger.from_date <= date)
& (Ledger.to_date >= date)
& (Ledger.docstatus == 1)
& (Ledger.transaction_type == "Leave Allocation")
& (Ledger.employee == employee)
& (Ledger.is_expired == 0)
& (Ledger.is_lwp == 0)
& (
# newly allocated leave's end date is same as the leave allocation's to date
((Ledger.is_carry_forward == 0) & (Ledger.to_date >= date))
# carry forwarded leave's end date won't be same as the leave allocation's to date
# it's between the leave allocation's from and to date
| (
(Ledger.is_carry_forward == 1)
& (Ledger.to_date.between(LeaveAllocation.from_date, LeaveAllocation.to_date))
)
)
)
)
@@ -925,8 +939,12 @@ def get_remaining_leaves(
# balance for carry forwarded leaves
if cf_expiry and allocation.unused_leaves:
cf_leaves = flt(allocation.unused_leaves) + flt(leaves_taken)
remaining_cf_leaves = _get_remaining_leaves(cf_leaves, cf_expiry)
if getdate(date) > getdate(cf_expiry):
# carry forwarded leave expiry date passed
cf_leaves = remaining_cf_leaves = 0
else:
cf_leaves = flt(allocation.unused_leaves) + flt(leaves_taken)
remaining_cf_leaves = _get_remaining_leaves(cf_leaves, cf_expiry)
leave_balance = flt(allocation.new_leaves_allocated) + flt(cf_leaves)
leave_balance_for_consumption = flt(allocation.new_leaves_allocated) + flt(remaining_cf_leaves)

View File

@@ -698,8 +698,7 @@ class TestLeaveApplication(unittest.TestCase):
leave_type_name="_Test_CF_leave_expiry",
is_carry_forward=1,
expire_carry_forwarded_leaves_after_days=90,
)
leave_type.insert()
).insert()
create_carry_forwarded_allocation(employee, leave_type)
details = get_leave_balance_on(
@@ -992,17 +991,51 @@ class TestLeaveApplication(unittest.TestCase):
self.assertEqual(leave_allocation, expected)
@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
def test_get_leave_allocation_records(self):
def test_leave_details_with_expired_cf_leaves(self):
employee = get_employee()
leave_type = create_leave_type(
leave_type_name="_Test_CF_leave_expiry",
is_carry_forward=1,
expire_carry_forwarded_leaves_after_days=90,
)
leave_type.insert()
).insert()
leave_alloc = create_carry_forwarded_allocation(employee, leave_type)
details = get_leave_allocation_records(employee.name, getdate(), leave_type.name)
cf_expiry = frappe.db.get_value(
"Leave Ledger Entry", {"transaction_name": leave_alloc.name, "is_carry_forward": 1}, "to_date"
)
# all leaves available before cf leave expiry
leave_details = get_leave_details(employee.name, add_days(cf_expiry, -1))
self.assertEqual(leave_details["leave_allocation"][leave_type.name]["remaining_leaves"], 30.0)
# cf leaves expired
leave_details = get_leave_details(employee.name, add_days(cf_expiry, 1))
expected_data = {
"total_leaves": 30.0,
"expired_leaves": 15.0,
"leaves_taken": 0.0,
"leaves_pending_approval": 0.0,
"remaining_leaves": 15.0,
}
self.assertEqual(leave_details["leave_allocation"][leave_type.name], expected_data)
@set_holiday_list("Salary Slip Test Holiday List", "_Test Company")
def test_get_leave_allocation_records(self):
"""Tests if total leaves allocated before and after carry forwarded leave expiry is same"""
employee = get_employee()
leave_type = create_leave_type(
leave_type_name="_Test_CF_leave_expiry",
is_carry_forward=1,
expire_carry_forwarded_leaves_after_days=90,
).insert()
leave_alloc = create_carry_forwarded_allocation(employee, leave_type)
cf_expiry = frappe.db.get_value(
"Leave Ledger Entry", {"transaction_name": leave_alloc.name, "is_carry_forward": 1}, "to_date"
)
# test total leaves allocated before cf leave expiry
details = get_leave_allocation_records(employee.name, add_days(cf_expiry, -1), leave_type.name)
expected_data = {
"from_date": getdate(leave_alloc.from_date),
"to_date": getdate(leave_alloc.to_date),
@@ -1013,6 +1046,11 @@ class TestLeaveApplication(unittest.TestCase):
}
self.assertEqual(details.get(leave_type.name), expected_data)
# test leaves allocated after carry forwarded leaves expiry, should be same thoroughout allocation period
# cf leaves should show up under expired or taken leaves later
details = get_leave_allocation_records(employee.name, add_days(cf_expiry, 1), leave_type.name)
self.assertEqual(details.get(leave_type.name), expected_data)
def create_carry_forwarded_allocation(employee, leave_type):
# initial leave allocation

View File

@@ -2,53 +2,60 @@
// License: GNU General Public License v3. See license.txt
frappe.query_reports["Employee Leave Balance"] = {
"filters": [
filters: [
{
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
"reqd": 1,
"default": frappe.defaults.get_default("year_start_date")
fieldname: "from_date",
label: __("From Date"),
fieldtype: "Date",
reqd: 1,
default: frappe.defaults.get_default("year_start_date")
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
"reqd": 1,
"default": frappe.defaults.get_default("year_end_date")
fieldname: "to_date",
label: __("To Date"),
fieldtype: "Date",
reqd: 1,
default: frappe.defaults.get_default("year_end_date")
},
{
"fieldname": "company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"reqd": 1,
"default": frappe.defaults.get_user_default("Company")
label: __("Company"),
fieldname: "company",
fieldtype: "Link",
options: "Company",
reqd: 1,
default: frappe.defaults.get_user_default("Company")
},
{
"fieldname": "department",
"label": __("Department"),
"fieldtype": "Link",
"options": "Department",
fieldname: "department",
label: __("Department"),
fieldtype: "Link",
options: "Department",
},
{
"fieldname": "employee",
"label": __("Employee"),
"fieldtype": "Link",
"options": "Employee",
fieldname: "employee",
label: __("Employee"),
fieldtype: "Link",
options: "Employee",
},
{
"fieldname": "employee_status",
"label": __("Employee Status"),
"fieldtype": "Select",
"options": [
fieldname: "employee_status",
label: __("Employee Status"),
fieldtype: "Select",
options: [
"",
{ "value": "Active", "label": __("Active") },
{ "value": "Inactive", "label": __("Inactive") },
{ "value": "Suspended", "label": __("Suspended") },
{ "value": "Left", "label": __("Left") },
],
"default": "Active",
default: "Active",
},
{
fieldname: "consolidate_leave_types",
label: __("Consolidate Leave Types"),
fieldtype: "Check",
default: 1,
depends_on: "eval: !doc.employee",
}
],

View File

@@ -7,7 +7,7 @@ from typing import Dict, List, Optional, Tuple
import frappe
from frappe import _
from frappe.utils import add_days, getdate
from frappe.utils import add_days, cint, getdate
from erpnext.hr.doctype.leave_allocation.leave_allocation import get_previous_allocation
from erpnext.hr.doctype.leave_application.leave_application import (
@@ -24,7 +24,7 @@ def execute(filters: Optional[Filters] = None) -> Tuple:
columns = get_columns()
data = get_data(filters)
charts = get_chart_data(data)
charts = get_chart_data(data, filters)
return columns, data, None, charts
@@ -89,7 +89,7 @@ def get_data(filters: Filters) -> List:
conditions = get_conditions(filters)
user = frappe.session.user
department_approver_map = get_department_leave_approver_map(filters.get("department"))
department_approver_map = get_department_leave_approver_map(filters.department)
active_employees = frappe.get_list(
"Employee",
@@ -97,22 +97,27 @@ def get_data(filters: Filters) -> List:
fields=["name", "employee_name", "department", "user_id", "leave_approver"],
)
precision = cint(frappe.db.get_single_value("System Settings", "float_precision", cache=True))
consolidate_leave_types = len(active_employees) > 1 and filters.consolidate_leave_types
row = None
data = []
for leave_type in leave_types:
if len(active_employees) > 1:
if consolidate_leave_types:
data.append({"leave_type": leave_type})
else:
row = frappe._dict({"leave_type": leave_type})
for employee in active_employees:
leave_approvers = department_approver_map.get(employee.department_name, []).append(
employee.leave_approver
)
if len(active_employees) > 1:
if consolidate_leave_types:
row = frappe._dict()
else:
row = frappe._dict({"leave_type": leave_type})
row.employee = employee.name
row.employee_name = employee.employee_name
@@ -166,17 +171,17 @@ def get_opening_balance(
def get_conditions(filters: Filters) -> Dict:
conditions = {}
if filters.get("employee"):
conditions["name"] = filters.get("employee")
if filters.employee:
conditions["name"] = filters.employee
if filters.get("company"):
conditions["company"] = filters.get("company")
if filters.company:
conditions["company"] = filters.company
if filters.get("department"):
conditions["department"] = filters.get("department")
if filters.department:
conditions["department"] = filters.department
if filters.get("employee_status"):
conditions["status"] = filters.get("employee_status")
if filters.employee_status:
conditions["status"] = filters.employee_status
return conditions
@@ -268,12 +273,15 @@ def get_leave_ledger_entries(
return records
def get_chart_data(data: List) -> Dict:
def get_chart_data(data: List, filters: Filters) -> Dict:
labels = []
datasets = []
employee_data = data
if data and data[0].get("employee_name"):
if not data:
return None
if data and filters.employee:
get_dataset_for_chart(employee_data, datasets, labels)
chart = {

View File

@@ -64,8 +64,6 @@
"fieldtype": "Section Break"
},
{
"fetch_from": "prevdoc_detail_docname.sales_person",
"fetch_if_empty": 1,
"fieldname": "service_person",
"fieldtype": "Link",
"in_list_view": 1,
@@ -110,13 +108,15 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2021-05-27 17:47:21.474282",
"modified": "2023-02-27 11:09:33.114458",
"modified_by": "Administrator",
"module": "Maintenance",
"name": "Maintenance Visit Purpose",
"naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -536,7 +536,34 @@ class JobCard(Document):
)
def set_transferred_qty_in_job_card_item(self, ste_doc):
from frappe.query_builder.functions import Sum
def _get_job_card_items_transferred_qty(ste_doc):
from frappe.query_builder.functions import Sum
job_card_items_transferred_qty = {}
job_card_items = [
x.get("job_card_item") for x in ste_doc.get("items") if x.get("job_card_item")
]
if job_card_items:
se = frappe.qb.DocType("Stock Entry")
sed = frappe.qb.DocType("Stock Entry Detail")
query = (
frappe.qb.from_(sed)
.join(se)
.on(sed.parent == se.name)
.select(sed.job_card_item, Sum(sed.qty))
.where(
(sed.job_card_item.isin(job_card_items))
& (se.docstatus == 1)
& (se.purpose == "Material Transfer for Manufacture")
)
.groupby(sed.job_card_item)
)
job_card_items_transferred_qty = frappe._dict(query.run(as_list=True))
return job_card_items_transferred_qty
def _validate_over_transfer(row, transferred_qty):
"Block over transfer of items if not allowed in settings."
@@ -553,29 +580,23 @@ class JobCard(Document):
exc=JobCardOverTransferError,
)
for row in ste_doc.items:
if not row.job_card_item:
continue
sed = frappe.qb.DocType("Stock Entry Detail")
se = frappe.qb.DocType("Stock Entry")
transferred_qty = (
frappe.qb.from_(sed)
.join(se)
.on(sed.parent == se.name)
.select(Sum(sed.qty))
.where(
(sed.job_card_item == row.job_card_item)
& (se.docstatus == 1)
& (se.purpose == "Material Transfer for Manufacture")
)
).run()[0][0]
job_card_items_transferred_qty = _get_job_card_items_transferred_qty(ste_doc)
if job_card_items_transferred_qty:
allow_excess = frappe.db.get_single_value("Manufacturing Settings", "job_card_excess_transfer")
if not allow_excess:
_validate_over_transfer(row, transferred_qty)
frappe.db.set_value("Job Card Item", row.job_card_item, "transferred_qty", flt(transferred_qty))
for row in ste_doc.items:
if not row.job_card_item:
continue
transferred_qty = flt(job_card_items_transferred_qty.get(row.job_card_item))
if not allow_excess:
_validate_over_transfer(row, transferred_qty)
frappe.db.set_value(
"Job Card Item", row.job_card_item, "transferred_qty", flt(transferred_qty)
)
def set_transferred_qty(self, update_status=False):
"Set total FG Qty in Job Card for which RM was transferred."

View File

@@ -25,8 +25,9 @@ frappe.query_reports["BOM Stock Report"] = {
],
"formatter": function(value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
if (column.id == "item") {
if (data["enough_parts_to_build"] > 0) {
if (data["in_stock_qty"] >= data["required_qty"]) {
value = `<a style='color:green' href="/app/item/${data['item']}" data-doctype="Item">${data['item']}</a>`;
} else {
value = `<a style='color:red' href="/app/item/${data['item']}" data-doctype="Item">${data['item']}</a>`;

View File

@@ -1,16 +1,61 @@
import frappe
from erpnext.regional.india.setup import make_custom_fields
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
def execute():
if frappe.get_all("Company", filters={"country": "India"}):
frappe.reload_doc("accounts", "doctype", "POS Invoice")
frappe.reload_doc("accounts", "doctype", "POS Invoice Item")
make_custom_fields()
custom_fields = get_non_profit_custom_fields()
create_custom_fields(custom_fields, update=True)
if not frappe.db.exists("Party Type", "Donor"):
frappe.get_doc(
{"doctype": "Party Type", "party_type": "Donor", "account_type": "Receivable"}
).insert(ignore_permissions=True)
).insert(ignore_permissions=True, ignore_mandatory=True)
def get_non_profit_custom_fields():
return {
"Company": [
{
"fieldname": "non_profit_section",
"label": "Non Profit Settings",
"fieldtype": "Section Break",
"insert_after": "asset_received_but_not_billed",
"collapsible": 1,
},
{
"fieldname": "company_80g_number",
"label": "80G Number",
"fieldtype": "Data",
"insert_after": "non_profit_section",
},
{
"fieldname": "with_effect_from",
"label": "80G With Effect From",
"fieldtype": "Date",
"insert_after": "company_80g_number",
},
{
"fieldname": "pan_details",
"label": "PAN Number",
"fieldtype": "Data",
"insert_after": "with_effect_from",
},
],
"Member": [
{
"fieldname": "pan_number",
"label": "PAN Details",
"fieldtype": "Data",
"insert_after": "email_id",
},
],
"Donor": [
{
"fieldname": "pan_number",
"label": "PAN Details",
"fieldtype": "Data",
"insert_after": "email",
},
],
}

View File

@@ -1,7 +1,6 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import datetime
import math
@@ -316,6 +315,8 @@ class SalarySlip(TransactionBase):
)
working_days = date_diff(self.end_date, self.start_date) + 1
working_days_list = [add_days(self.start_date, i) for i in range(working_days)]
if for_preview:
self.total_working_days = working_days
self.payment_days = working_days
@@ -325,6 +326,8 @@ class SalarySlip(TransactionBase):
if not cint(include_holidays_in_total_working_days):
working_days -= len(holidays)
working_days_list = [cstr(day) for day in working_days_list if cstr(day) not in holidays]
if working_days < 0:
frappe.throw(_("There are more holidays than working days this month."))
@@ -335,7 +338,7 @@ class SalarySlip(TransactionBase):
actual_lwp, absent = self.calculate_lwp_ppl_and_absent_days_based_on_attendance(holidays)
self.absent_days = absent
else:
actual_lwp = self.calculate_lwp_or_ppl_based_on_leave_application(holidays, working_days)
actual_lwp = self.calculate_lwp_or_ppl_based_on_leave_application(holidays, working_days_list)
if not lwp:
lwp = actual_lwp
@@ -458,16 +461,15 @@ class SalarySlip(TransactionBase):
def get_holidays_for_employee(self, start_date, end_date):
return get_holiday_dates_for_employee(self.employee, start_date, end_date)
def calculate_lwp_or_ppl_based_on_leave_application(self, holidays, working_days):
def calculate_lwp_or_ppl_based_on_leave_application(self, holidays, working_days_list):
lwp = 0
holidays = "','".join(holidays)
daily_wages_fraction_for_half_day = (
flt(frappe.db.get_value("Payroll Settings", None, "daily_wages_fraction_for_half_day")) or 0.5
)
for d in range(working_days):
date = add_days(cstr(getdate(self.start_date)), d)
leave = get_lwp_or_ppl_for_date(date, self.employee, holidays)
for d in working_days_list:
leave = get_lwp_or_ppl_for_date(d, self.employee, holidays)
if leave:
equivalent_lwp_count = 0

View File

@@ -119,7 +119,16 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
frappe.model.round_floats_in(item);
item.net_rate = item.rate;
item.qty = item.qty === undefined ? (me.frm.doc.is_return ? -1 : 1) : item.qty;
item.net_amount = item.amount = flt(item.rate * item.qty, precision("amount", item));
if (!(me.frm.doc.is_return || me.frm.doc.is_debit_note)) {
item.net_amount = item.amount = flt(item.rate * item.qty, precision("amount", item));
}
else {
// allow for '0' qty on Credit/Debit notes
let qty = item.qty || -1
item.net_amount = item.amount = flt(item.rate * qty, precision("amount", item));
}
item.item_tax_amount = 0.0;
item.total_weight = flt(item.weight_per_unit * item.stock_qty);

View File

@@ -1975,11 +1975,13 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
get_advances: function() {
if(!this.frm.is_return) {
var me = this;
return this.frm.call({
method: "set_advances",
doc: this.frm.doc,
callback: function(r, rt) {
refresh_field("advances");
me.frm.dirty();
}
})
}

View File

@@ -84,11 +84,15 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
}
if (doc.docstatus == 1 && !["Lost", "Ordered"].includes(doc.status)) {
this.frm.add_custom_button(
__("Sales Order"),
this.frm.cscript["Make Sales Order"],
__("Create")
);
if (frappe.boot.sysdefaults.allow_sales_order_creation_for_expired_quotation
|| (!doc.valid_till)
|| frappe.datetime.get_diff(doc.valid_till, frappe.datetime.get_today()) >= 0) {
this.frm.add_custom_button(
__("Sales Order"),
this.frm.cscript["Make Sales Order"],
__("Create")
);
}
if(doc.status!=="Ordered") {
this.frm.add_custom_button(__('Set as Lost'), () => {

View File

@@ -192,6 +192,17 @@ def get_list_context(context=None):
@frappe.whitelist()
def make_sales_order(source_name: str, target_doc=None):
if not frappe.db.get_singles_value(
"Selling Settings", "allow_sales_order_creation_for_expired_quotation"
):
quotation = frappe.db.get_value(
"Quotation", source_name, ["transaction_date", "valid_till"], as_dict=1
)
if quotation.valid_till and (
quotation.valid_till < quotation.transaction_date or quotation.valid_till < getdate(nowdate())
):
frappe.throw(_("Validity period of this quotation has ended."))
return _make_sales_order(source_name, target_doc)

View File

@@ -126,11 +126,21 @@ class TestQuotation(FrappeTestCase):
def test_so_from_expired_quotation(self):
from erpnext.selling.doctype.quotation.quotation import make_sales_order
frappe.db.set_single_value(
"Selling Settings", "allow_sales_order_creation_for_expired_quotation", 0
)
quotation = frappe.copy_doc(test_records[0])
quotation.valid_till = add_days(nowdate(), -1)
quotation.insert()
quotation.submit()
self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name)
frappe.db.set_single_value(
"Selling Settings", "allow_sales_order_creation_for_expired_quotation", 1
)
make_sales_order(quotation.name)
def test_shopping_cart_without_website_item(self):

View File

@@ -280,9 +280,12 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
make_work_order() {
var me = this;
this.frm.call({
doc: this.frm.doc,
method: 'get_work_order_items',
me.frm.call({
method: "erpnext.selling.doctype.sales_order.sales_order.get_work_order_items",
args: {
sales_order: this.frm.docname,
},
freeze: true,
callback: function(r) {
if(!r.message) {
frappe.msgprint({
@@ -292,14 +295,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
});
return;
}
else if(!r.message) {
frappe.msgprint({
title: __('Work Order not created'),
message: __('Work Order already created for all items with BOM'),
indicator: 'orange'
});
return;
} else {
else {
const fields = [{
label: 'Items',
fieldtype: 'Table',
@@ -400,9 +396,9 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
make_raw_material_request: function() {
var me = this;
this.frm.call({
doc: this.frm.doc,
method: 'get_work_order_items',
method: "erpnext.selling.doctype.sales_order.sales_order.get_work_order_items",
args: {
sales_order: this.frm.docname,
for_raw_material_request: 1
},
callback: function(r) {
@@ -421,6 +417,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
},
make_raw_material_request_dialog: function(r) {
var me = this;
var fields = [
{fieldtype:'Check', fieldname:'include_exploded_items',
label: __('Include Exploded Items')},

View File

@@ -6,11 +6,12 @@ import json
import frappe
import frappe.utils
from frappe import _
from frappe import _, qb
from frappe.contacts.doctype.address.address import get_company_address
from frappe.desk.notifications import clear_doctype_notifications
from frappe.model.mapper import get_mapped_doc
from frappe.model.utils import get_fetch_values
from frappe.query_builder.functions import Sum
from frappe.utils import add_days, cint, cstr, flt, get_link_to_form, getdate, nowdate, strip_html
from six import string_types
@@ -481,51 +482,6 @@ class SalesOrder(SellingController):
self.indicator_color = "green"
self.indicator_title = _("Paid")
@frappe.whitelist()
def get_work_order_items(self, for_raw_material_request=0):
"""Returns items with BOM that already do not have a linked work order"""
items = []
item_codes = [i.item_code for i in self.items]
product_bundle_parents = [
pb.new_item_code
for pb in frappe.get_all(
"Product Bundle", {"new_item_code": ["in", item_codes]}, ["new_item_code"]
)
]
for table in [self.items, self.packed_items]:
for i in table:
bom = get_default_bom(i.item_code)
stock_qty = i.qty if i.doctype == "Packed Item" else i.stock_qty
if not for_raw_material_request:
total_work_order_qty = flt(
frappe.db.sql(
"""select sum(qty) from `tabWork Order`
where production_item=%s and sales_order=%s and sales_order_item = %s and docstatus<2""",
(i.item_code, self.name, i.name),
)[0][0]
)
pending_qty = stock_qty - total_work_order_qty
else:
pending_qty = stock_qty
if pending_qty and i.item_code not in product_bundle_parents:
items.append(
dict(
name=i.name,
item_code=i.item_code,
description=i.description,
bom=bom or "",
warehouse=i.warehouse,
pending_qty=pending_qty,
required_qty=pending_qty if for_raw_material_request else 0,
sales_order_item=i.name,
)
)
return items
def on_recurring(self, reference_doc, auto_repeat_doc):
def _get_delivery_date(ref_doc_delivery_date, red_doc_transaction_date, transaction_date):
delivery_date = auto_repeat_doc.get_next_schedule_date(schedule_date=ref_doc_delivery_date)
@@ -1399,3 +1355,57 @@ def update_produced_qty_in_so_item(sales_order, sales_order_item):
return
frappe.db.set_value("Sales Order Item", sales_order_item, "produced_qty", total_produced_qty)
@frappe.whitelist()
def get_work_order_items(sales_order, for_raw_material_request=0):
"""Returns items with BOM that already do not have a linked work order"""
if sales_order:
so = frappe.get_doc("Sales Order", sales_order)
wo = qb.DocType("Work Order")
items = []
item_codes = [i.item_code for i in so.items]
product_bundle_parents = [
pb.new_item_code
for pb in frappe.get_all(
"Product Bundle", {"new_item_code": ["in", item_codes]}, ["new_item_code"]
)
]
for table in [so.items, so.packed_items]:
for i in table:
bom = get_default_bom(i.item_code)
stock_qty = i.qty if i.doctype == "Packed Item" else i.stock_qty
if not for_raw_material_request:
total_work_order_qty = flt(
qb.from_(wo)
.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))
)
.run()[0][0]
)
pending_qty = stock_qty - total_work_order_qty
else:
pending_qty = stock_qty
if pending_qty and i.item_code not in product_bundle_parents:
items.append(
dict(
name=i.name,
item_code=i.item_code,
description=i.description,
bom=bom or "",
warehouse=i.warehouse,
pending_qty=pending_qty,
required_qty=pending_qty if for_raw_material_request else 0,
sales_order_item=i.name,
)
)
return items

View File

@@ -1211,6 +1211,8 @@ class TestSalesOrder(FrappeTestCase):
self.assertTrue(si.get("payment_schedule"))
def test_make_work_order(self):
from erpnext.selling.doctype.sales_order.sales_order import get_work_order_items
# Make a new Sales Order
so = make_sales_order(
**{
@@ -1224,7 +1226,7 @@ class TestSalesOrder(FrappeTestCase):
# Raise Work Orders
po_items = []
so_item_name = {}
for item in so.get_work_order_items():
for item in get_work_order_items(so.name):
po_items.append(
{
"warehouse": item.get("warehouse"),
@@ -1415,6 +1417,7 @@ class TestSalesOrder(FrappeTestCase):
from erpnext.controllers.item_variant import create_variant
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
from erpnext.selling.doctype.sales_order.sales_order import get_work_order_items
make_item( # template item
"Test-WO-Tshirt",
@@ -1454,7 +1457,7 @@ class TestSalesOrder(FrappeTestCase):
]
}
)
wo_items = so.get_work_order_items()
wo_items = get_work_order_items(so.name)
self.assertEqual(wo_items[0].get("item_code"), "Test-WO-Tshirt-R")
self.assertEqual(wo_items[0].get("bom"), red_var_bom.name)
@@ -1464,6 +1467,8 @@ class TestSalesOrder(FrappeTestCase):
self.assertEqual(wo_items[1].get("bom"), template_bom.name)
def test_request_for_raw_materials(self):
from erpnext.selling.doctype.sales_order.sales_order import get_work_order_items
item = make_item(
"_Test Finished Item",
{
@@ -1496,7 +1501,7 @@ class TestSalesOrder(FrappeTestCase):
so = make_sales_order(**{"item_list": [{"item_code": item.item_code, "qty": 1, "rate": 1000}]})
so.submit()
mr_dict = frappe._dict()
items = so.get_work_order_items(1)
items = get_work_order_items(so.name, 1)
mr_dict["items"] = items
mr_dict["include_exploded_items"] = 0
mr_dict["ignore_existing_ordered_qty"] = 1

View File

@@ -32,7 +32,8 @@
"sales_update_frequency",
"allow_multiple_items",
"allow_against_multiple_purchase_orders",
"hide_tax_id"
"hide_tax_id",
"allow_sales_order_creation_for_expired_quotation"
],
"fields": [
{
@@ -199,6 +200,12 @@
"fieldtype": "Select",
"label": "Contract Naming By",
"options": "Party Name\nNaming Series"
},
{
"default": "0",
"fieldname": "allow_sales_order_creation_for_expired_quotation",
"fieldtype": "Check",
"label": "Allow Sales Order Creation For Expired Quotation"
}
],
"icon": "fa fa-cog",
@@ -206,7 +213,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2022-03-28 12:18:06.768403",
"modified": "2023-02-04 12:37:53.380857",
"modified_by": "Administrator",
"module": "Selling",
"name": "Selling Settings",

View File

@@ -522,7 +522,7 @@ erpnext.PointOfSale.Controller = class {
const from_selector = field === 'qty' && value === "+1";
if (from_selector)
value = flt(item_row.qty) + flt(value);
value = flt(item_row.stock_qty) + flt(value);
if (item_row_exists) {
if (field === 'qty')

View File

@@ -41,8 +41,20 @@ def get_columns(filters):
{"label": _("Description"), "fieldtype": "Data", "fieldname": "description", "width": 150},
{"label": _("Quantity"), "fieldtype": "Float", "fieldname": "quantity", "width": 150},
{"label": _("UOM"), "fieldtype": "Link", "fieldname": "uom", "options": "UOM", "width": 100},
{"label": _("Rate"), "fieldname": "rate", "options": "Currency", "width": 120},
{"label": _("Amount"), "fieldname": "amount", "options": "Currency", "width": 120},
{
"label": _("Rate"),
"fieldname": "rate",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Amount"),
"fieldname": "amount",
"fieldtype": "Currency",
"options": "currency",
"width": 120,
},
{
"label": _("Sales Order"),
"fieldtype": "Link",
@@ -93,8 +105,9 @@ def get_columns(filters):
},
{
"label": _("Billed Amount"),
"fieldtype": "currency",
"fieldtype": "Currency",
"fieldname": "billed_amount",
"options": "currency",
"width": 120,
},
{
@@ -104,6 +117,13 @@ def get_columns(filters):
"options": "Company",
"width": 100,
},
{
"label": _("Currency"),
"fieldtype": "Link",
"fieldname": "currency",
"options": "Currency",
"hidden": 1,
},
]
@@ -141,31 +161,12 @@ def get_data(filters):
"billed_amount": flt(record.get("billed_amt")),
"company": record.get("company"),
}
row["currency"] = frappe.get_cached_value("Company", row["company"], "default_currency")
data.append(row)
return data
def get_conditions(filters):
conditions = ""
if filters.get("item_group"):
conditions += "AND so_item.item_group = %s" % frappe.db.escape(filters.item_group)
if filters.get("from_date"):
conditions += "AND so.transaction_date >= '%s'" % filters.from_date
if filters.get("to_date"):
conditions += "AND so.transaction_date <= '%s'" % filters.to_date
if filters.get("item_code"):
conditions += "AND so_item.item_code = %s" % frappe.db.escape(filters.item_code)
if filters.get("customer"):
conditions += "AND so.customer = %s" % frappe.db.escape(filters.customer)
return conditions
def get_customer_details():
details = frappe.get_all("Customer", fields=["name", "customer_name", "customer_group"])
customer_details = {}
@@ -187,29 +188,50 @@ def get_item_details():
def get_sales_order_details(company_list, filters):
conditions = get_conditions(filters)
db_so = frappe.qb.DocType("Sales Order")
db_so_item = frappe.qb.DocType("Sales Order Item")
return frappe.db.sql(
"""
SELECT
so_item.item_code, so_item.description, so_item.qty,
so_item.uom, so_item.base_rate, so_item.base_amount,
so.name, so.transaction_date, so.customer,so.territory,
so.project, so_item.delivered_qty,
so_item.billed_amt, so.company
FROM
`tabSales Order` so, `tabSales Order Item` so_item
WHERE
so.name = so_item.parent
AND so.company in ({0})
AND so.docstatus = 1 {1}
""".format(
",".join(["%s"] * len(company_list)), conditions
),
tuple(company_list),
as_dict=1,
query = (
frappe.qb.from_(db_so)
.inner_join(db_so_item)
.on(db_so_item.parent == db_so.name)
.select(
db_so.name,
db_so.customer,
db_so.transaction_date,
db_so.territory,
db_so.project,
db_so.company,
db_so_item.item_code,
db_so_item.description,
db_so_item.qty,
db_so_item.uom,
db_so_item.base_rate,
db_so_item.base_amount,
db_so_item.delivered_qty,
(db_so_item.billed_amt * db_so.conversion_rate).as_("billed_amt"),
)
.where(db_so.docstatus == 1)
.where(db_so.company.isin(tuple(company_list)))
)
if filters.get("item_group"):
query = query.where(db_so_item.item_group == filters.item_group)
if filters.get("from_date"):
query = query.where(db_so.transaction_date >= filters.from_date)
if filters.get("to_date"):
query = query.where(db_so.transaction_date <= filters.to_date)
if filters.get("item_code"):
query = query.where(db_so_item.item_code == filters.item_code)
if filters.get("customer"):
query = query.where(db_so.customer == filters.customer)
return query.run(as_dict=1)
def get_chart_data(data):
item_wise_sales_map = {}

View File

@@ -417,9 +417,14 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
args: args,
callback: function(r) {
if(r.message) {
frappe.model.set_value(doc.doctype, doc.name, 'batch_no', r.message);
} else {
frappe.model.set_value(doc.doctype, doc.name, 'batch_no', r.message);
if (r.message.batch_no != null) {
frappe.model.set_value(doc.doctype, doc.name, 'batch_no', r.message.batch_no);
} else if (r.message.msg_print) {
frappe.show_alert({
message: r.message.msg_print,
indicator:'orange'
}, 5);
}
}
}
});

View File

@@ -26,6 +26,12 @@ def boot_session(bootinfo):
frappe.db.get_single_value("Selling Settings", "default_valid_till")
)
bootinfo.sysdefaults.allow_sales_order_creation_for_expired_quotation = cint(
frappe.db.get_single_value(
"Selling Settings", "allow_sales_order_creation_for_expired_quotation"
)
)
# if no company, show a dialog box to create a new company
bootinfo.customer_count = frappe.db.sql("""SELECT count(*) FROM `tabCustomer`""")[0][0]

View File

@@ -42,7 +42,7 @@ erpnext.stock.ItemDashboard = Class.extend({
let warehouse = unescape(element.attr('data-warehouse'));
let actual_qty = unescape(element.attr('data-actual_qty'));
let disable_quick_entry = Number(unescape(element.attr('data-disable_quick_entry')));
let entry_type = action === "Move" ? "Material Transfer" : null;
let entry_type = action === "Move" ? "Material Transfer" : "Material Receipt";
if (disable_quick_entry) {
open_stock_entry(item, warehouse, entry_type);
@@ -63,11 +63,19 @@ erpnext.stock.ItemDashboard = Class.extend({
function open_stock_entry(item, warehouse, entry_type) {
frappe.model.with_doctype('Stock Entry', function () {
var doc = frappe.model.get_new_doc('Stock Entry');
if (entry_type) doc.stock_entry_type = entry_type;
if (entry_type) {
doc.stock_entry_type = entry_type;
}
var row = frappe.model.add_child(doc, 'items');
row.item_code = item;
row.s_warehouse = warehouse;
if (entry_type === "Material Transfer") {
row.s_warehouse = warehouse;
}
else {
row.t_warehouse = warehouse;
}
frappe.set_route('Form', doc.doctype, doc.name);
});

View File

@@ -260,7 +260,9 @@ def set_batch_nos(doc, warehouse_field, throw=False, child_table="items"):
warehouse = d.get(warehouse_field, None)
if warehouse and qty > 0 and frappe.db.get_value("Item", d.item_code, "has_batch_no"):
if not d.batch_no:
d.batch_no = get_batch_no(d.item_code, warehouse, qty, throw, d.serial_no)
d.batch_no = get_batch_no(d.item_code, warehouse, qty, throw, d.serial_no).get(
"batch_no", None
)
else:
batch_qty = get_batch_qty(batch_no=d.batch_no, warehouse=warehouse)
if flt(batch_qty, d.precision("qty")) < flt(qty, d.precision("qty")):
@@ -282,6 +284,7 @@ def get_batch_no(item_code, warehouse, qty=1, throw=False, serial_no=None):
"""
batch_no = None
message = None
batches = get_batches(item_code, warehouse, qty, throw, serial_no)
for batch in batches:
@@ -290,15 +293,18 @@ def get_batch_no(item_code, warehouse, qty=1, throw=False, serial_no=None):
break
if not batch_no:
frappe.msgprint(
_(
"Please select a Batch for Item {0}. Unable to find a single batch that fulfills this requirement"
).format(frappe.bold(item_code))
)
message = _(
"Please select a Batch for Item {0}. Unable to find a single batch that fulfills this requirement"
).format(frappe.bold(item_code))
if throw:
frappe.msgprint(
_(
"Please select a Batch for Item {0}. Unable to find a single batch that fulfills this requirement"
).format(frappe.bold(item_code))
)
raise UnableToSelectBatchError
return batch_no
return {"batch_no": batch_no, "msg_print": message}
def get_batches(item_code, warehouse, qty=1, throw=False, serial_no=None):

View File

@@ -99,7 +99,8 @@ class TestBatch(FrappeTestCase):
# shipped from FEFO batch
self.assertEqual(
delivery_note.items[0].batch_no, get_batch_no(item_code, receipt.items[0].warehouse, batch_qty)
delivery_note.items[0].batch_no,
get_batch_no(item_code, receipt.items[0].warehouse, batch_qty).get("batch_no", None),
)
def test_delivery_note_fail(self):
@@ -145,7 +146,8 @@ class TestBatch(FrappeTestCase):
# assert same batch is selected
self.assertEqual(
stock_entry.items[0].batch_no, get_batch_no(item_code, receipt.items[0].warehouse, batch_qty)
stock_entry.items[0].batch_no,
get_batch_no(item_code, receipt.items[0].warehouse, batch_qty).get("batch_no", None),
)
def test_batch_split(self):

View File

@@ -150,12 +150,17 @@ def update_qty(bin_name, args):
last_sle_qty = (
frappe.qb.from_(sle)
.select(sle.qty_after_transaction)
.where((sle.item_code == args.get("item_code")) & (sle.warehouse == args.get("warehouse")))
.where(
(sle.item_code == args.get("item_code"))
& (sle.warehouse == args.get("warehouse"))
& (sle.is_cancelled == 0)
)
.orderby(CombineDatetime(sle.posting_date, sle.posting_time), order=Order.desc)
.orderby(sle.creation, order=Order.desc)
.run()
)
actual_qty = 0.0
if last_sle_qty:
actual_qty = last_sle_qty[0][0]

View File

@@ -97,12 +97,12 @@ frappe.ui.form.on("Delivery Note", {
}
if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) {
let internal = me.frm.doc.is_internal_customer;
let internal = frm.doc.is_internal_customer;
if (internal) {
let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Receipt" :
let button_label = (frm.doc.company === frm.doc.represents_company) ? "Internal Purchase Receipt" :
"Inter Company Purchase Receipt";
me.frm.add_custom_button(button_label, function() {
frm.add_custom_button(__(button_label), function() {
frappe.model.open_mapped_doc({
method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
frm: frm,

View File

@@ -33,6 +33,9 @@ frappe.ui.form.on("Item", {
'Material Request': () => {
open_form(frm, "Material Request", "Material Request Item", "items");
},
'Stock Entry': () => {
open_form(frm, "Stock Entry", "Stock Entry Detail", "items");
},
};
},
@@ -848,6 +851,9 @@ function open_form(frm, doctype, child_doctype, parentfield) {
new_child_doc.item_name = frm.doc.item_name;
new_child_doc.uom = frm.doc.stock_uom;
new_child_doc.description = frm.doc.description;
if (!new_child_doc.qty) {
new_child_doc.qty = 1.0;
}
frappe.run_serially([
() => frappe.ui.form.make_quick_entry(doctype, null, null, new_doc),

View File

@@ -955,7 +955,8 @@
"image_field": "image",
"index_web_pages_for_search": 1,
"links": [],
"modified": "2022-04-28 04:52:10.272256",
"make_attachments_public": 1,
"modified": "2022-09-13 04:08:17.431731",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@@ -2,7 +2,18 @@
// License: GNU General Public License v3. See license.txt
frappe.ui.form.on("Item Price", {
onload: function (frm) {
setup(frm) {
frm.set_query("item_code", function() {
return {
filters: {
"disabled": 0,
"has_variants": 0
}
};
});
},
onload(frm) {
// Fetch price list details
frm.add_fetch("price_list", "buying", "buying");
frm.add_fetch("price_list", "selling", "selling");

View File

@@ -3,7 +3,7 @@
import frappe
from frappe import _
from frappe import _, bold
from frappe.model.document import Document
from frappe.utils import getdate
@@ -19,6 +19,7 @@ class ItemPrice(Document):
self.update_price_list_details()
self.update_item_details()
self.check_duplicates()
self.validate_item_template()
def validate_item(self):
if not frappe.db.exists("Item", self.item_code):
@@ -47,6 +48,12 @@ class ItemPrice(Document):
"Item", self.item_code, ["item_name", "description"]
)
def validate_item_template(self):
if frappe.get_cached_value("Item", self.item_code, "has_variants"):
msg = f"Item Price cannot be created for the template item {bold(self.item_code)}"
frappe.throw(_(msg))
def check_duplicates(self):
conditions = (
"""where item_code = %(item_code)s and price_list = %(price_list)s and name != %(name)s"""

View File

@@ -16,6 +16,28 @@ class TestItemPrice(FrappeTestCase):
frappe.db.sql("delete from `tabItem Price`")
make_test_records_for_doctype("Item Price", force=True)
def test_template_item_price(self):
from erpnext.stock.doctype.item.test_item import make_item
item = make_item(
"Test Template Item 1",
{
"has_variants": 1,
"variant_based_on": "Manufacturer",
},
)
doc = frappe.get_doc(
{
"doctype": "Item Price",
"price_list": "_Test Price List",
"item_code": item.name,
"price_list_rate": 100,
}
)
self.assertRaises(frappe.ValidationError, doc.save)
def test_duplicate_item(self):
doc = frappe.copy_doc(test_records[0])
self.assertRaises(ItemPriceDuplicateItem, doc.save)

View File

@@ -38,5 +38,19 @@
"price_list_rate": 1000,
"valid_from": "2017-04-10",
"valid_upto": "2017-04-17"
},
{
"doctype": "Item Price",
"item_code": "_Test Item",
"price_list": "_Test Buying Price List",
"price_list_rate": 100,
"supplier": "_Test Supplier"
},
{
"doctype": "Item Price",
"item_code": "_Test Item",
"price_list": "_Test Selling Price List",
"price_list_rate": 200,
"customer": "_Test Customer"
}
]

View File

@@ -55,7 +55,6 @@ class LandedCostVoucher(Document):
self.get_items_from_purchase_receipts()
self.set_applicable_charges_on_item()
self.validate_applicable_charges_for_item()
def check_mandatory(self):
if not self.get("purchase_receipts"):
@@ -115,6 +114,13 @@ class LandedCostVoucher(Document):
total_item_cost += item.get(based_on_field)
for item in self.get("items"):
if not total_item_cost and not item.get(based_on_field):
frappe.throw(
_(
"It's not possible to distribute charges equally when total amount is zero, please set 'Distribute Charges Based On' as 'Quantity'"
)
)
item.applicable_charges = flt(
flt(item.get(based_on_field)) * (flt(self.total_taxes_and_charges) / flt(total_item_cost)),
item.precision("applicable_charges"),
@@ -162,6 +168,7 @@ class LandedCostVoucher(Document):
)
def on_submit(self):
self.validate_applicable_charges_for_item()
self.update_landed_cost()
def on_cancel(self):

View File

@@ -175,6 +175,59 @@ class TestLandedCostVoucher(FrappeTestCase):
)
self.assertEqual(last_sle_after_landed_cost.stock_value - last_sle.stock_value, 50.0)
def test_landed_cost_voucher_for_zero_purchase_rate(self):
"Test impact of LCV on future stock balances."
from erpnext.stock.doctype.item.test_item import make_item
item = make_item("LCV Stock Item", {"is_stock_item": 1})
warehouse = "Stores - _TC"
pr = make_purchase_receipt(
item_code=item.name,
warehouse=warehouse,
qty=10,
rate=0,
posting_date=add_days(frappe.utils.nowdate(), -2),
)
self.assertEqual(
frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name, "is_cancelled": 0},
"stock_value_difference",
),
0,
)
lcv = make_landed_cost_voucher(
company=pr.company,
receipt_document_type="Purchase Receipt",
receipt_document=pr.name,
charges=100,
distribute_charges_based_on="Distribute Manually",
do_not_save=True,
)
lcv.get_items_from_purchase_receipts()
lcv.items[0].applicable_charges = 100
lcv.save()
lcv.submit()
self.assertTrue(
frappe.db.exists(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name, "is_cancelled": 0},
)
)
self.assertEqual(
frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name, "is_cancelled": 0},
"stock_value_difference",
),
100,
)
def test_landed_cost_voucher_against_purchase_invoice(self):
pi = make_purchase_invoice(
@@ -516,7 +569,7 @@ def make_landed_cost_voucher(**args):
lcv = frappe.new_doc("Landed Cost Voucher")
lcv.company = args.company or "_Test Company"
lcv.distribute_charges_based_on = "Amount"
lcv.distribute_charges_based_on = args.distribute_charges_based_on or "Amount"
lcv.set(
"purchase_receipts",

View File

@@ -366,10 +366,11 @@ frappe.ui.form.on('Material Request', {
frappe.ui.form.on("Material Request Item", {
qty: function (frm, doctype, name) {
var d = locals[doctype][name];
if (flt(d.qty) < flt(d.min_order_qty)) {
const item = locals[doctype][name];
if (flt(item.qty) < flt(item.min_order_qty)) {
frappe.msgprint(__("Warning: Material Requested Qty is less than Minimum Order Qty"));
}
frm.events.get_item_data(frm, item, false);
},
from_warehouse: function(frm, doctype, name) {

View File

@@ -10,6 +10,7 @@ import json
import frappe
from frappe import _, msgprint
from frappe.model.mapper import get_mapped_doc
from frappe.query_builder.functions import Sum
from frappe.utils import cint, cstr, flt, get_link_to_form, getdate, new_line_sep, nowdate
from six import string_types
@@ -185,6 +186,34 @@ class MaterialRequest(BuyingController):
self.update_requested_qty()
self.update_requested_qty_in_production_plan()
def get_mr_items_ordered_qty(self, mr_items):
mr_items_ordered_qty = {}
mr_items = [d.name for d in self.get("items") if d.name in mr_items]
doctype = qty_field = None
if self.material_request_type in ("Material Issue", "Material Transfer", "Customer Provided"):
doctype = frappe.qb.DocType("Stock Entry Detail")
qty_field = doctype.transfer_qty
elif self.material_request_type == "Manufacture":
doctype = frappe.qb.DocType("Work Order")
qty_field = doctype.qty
if doctype and qty_field:
query = (
frappe.qb.from_(doctype)
.select(doctype.material_request_item, Sum(qty_field))
.where(
(doctype.material_request == self.name)
& (doctype.material_request_item.isin(mr_items))
& (doctype.docstatus == 1)
)
.groupby(doctype.material_request_item)
)
mr_items_ordered_qty = frappe._dict(query.run())
return mr_items_ordered_qty
def update_completed_qty(self, mr_items=None, update_modified=True):
if self.material_request_type == "Purchase":
return
@@ -192,18 +221,13 @@ class MaterialRequest(BuyingController):
if not mr_items:
mr_items = [d.name for d in self.get("items")]
mr_items_ordered_qty = self.get_mr_items_ordered_qty(mr_items)
mr_qty_allowance = frappe.db.get_single_value("Stock Settings", "mr_qty_allowance")
for d in self.get("items"):
if d.name in mr_items:
if self.material_request_type in ("Material Issue", "Material Transfer", "Customer Provided"):
d.ordered_qty = flt(
frappe.db.sql(
"""select sum(transfer_qty)
from `tabStock Entry Detail` where material_request = %s
and material_request_item = %s and docstatus = 1""",
(self.name, d.name),
)[0][0]
)
mr_qty_allowance = frappe.db.get_single_value("Stock Settings", "mr_qty_allowance")
d.ordered_qty = flt(mr_items_ordered_qty.get(d.name))
if mr_qty_allowance:
allowed_qty = d.qty + (d.qty * (mr_qty_allowance / 100))
@@ -224,14 +248,7 @@ class MaterialRequest(BuyingController):
)
elif self.material_request_type == "Manufacture":
d.ordered_qty = flt(
frappe.db.sql(
"""select sum(qty)
from `tabWork Order` where material_request = %s
and material_request_item = %s and docstatus = 1""",
(self.name, d.name),
)[0][0]
)
d.ordered_qty = flt(mr_items_ordered_qty.get(d.name))
frappe.db.set_value(d.doctype, d.name, "ordered_qty", d.ordered_qty)
@@ -594,6 +611,9 @@ def make_stock_entry(source_name, target_doc=None):
def set_missing_values(source, target):
target.purpose = source.material_request_type
target.from_warehouse = source.set_from_warehouse
target.to_warehouse = source.set_warehouse
if source.job_card:
target.purpose = "Material Transfer for Manufacture"

View File

@@ -31,5 +31,21 @@
"enabled": 1,
"price_list_name": "_Test Price List Rest of the World",
"selling": 1
},
{
"buying": 0,
"currency": "USD",
"doctype": "Price List",
"enabled": 1,
"price_list_name": "_Test Selling Price List",
"selling": 1
},
{
"buying": 1,
"currency": "USD",
"doctype": "Price List",
"enabled": 1,
"price_list_name": "_Test Buying Price List",
"selling": 0
}
]

View File

@@ -436,7 +436,7 @@ class PurchaseReceipt(BuyingController):
)
divisional_loss = flt(
valuation_amount_as_per_doc - stock_value_diff, d.precision("base_net_amount")
valuation_amount_as_per_doc - flt(stock_value_diff), d.precision("base_net_amount")
)
if divisional_loss:
@@ -1064,13 +1064,25 @@ def get_item_account_wise_additional_cost(purchase_document):
account.expense_account, {"amount": 0.0, "base_amount": 0.0}
)
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account][
"amount"
] += (account.amount * item.get(based_on_field) / total_item_cost)
if total_item_cost > 0:
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][
account.expense_account
]["amount"] += (
account.amount * item.get(based_on_field) / total_item_cost
)
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account][
"base_amount"
] += (account.base_amount * item.get(based_on_field) / total_item_cost)
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][
account.expense_account
]["base_amount"] += (
account.base_amount * item.get(based_on_field) / total_item_cost
)
else:
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][
account.expense_account
]["amount"] += item.applicable_charges
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][
account.expense_account
]["base_amount"] += item.applicable_charges
return item_account_wise_cost

View File

@@ -1262,7 +1262,9 @@ class StockEntry(StockController):
and ret.get("has_batch_no")
and not args.get("batch_no")
):
args.batch_no = get_batch_no(args["item_code"], args["s_warehouse"], args["qty"])
args.batch_no = get_batch_no(args["item_code"], args["s_warehouse"], args["qty"]).get(
"batch_no", None
)
if (
self.purpose == "Send to Subcontractor" and self.get("purchase_order") and args.get("item_code")
@@ -2440,7 +2442,7 @@ def get_uom_details(item_code, uom, qty):
if not conversion_factor:
frappe.msgprint(
_("UOM coversion factor required for UOM: {0} in Item: {1}").format(uom, item_code)
_("UOM conversion factor required for UOM: {0} in Item: {1}").format(uom, item_code)
)
ret = {"uom": ""}
else:

View File

@@ -1571,6 +1571,48 @@ class TestStockEntry(FrappeTestCase):
self.assertRaises(BatchExpiredError, se.save)
def test_negative_stock_reco(self):
from erpnext.controllers.stock_controller import BatchExpiredError
from erpnext.stock.doctype.batch.test_batch import make_new_batch
frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 0)
item_code = "Test Negative Item - 001"
item_doc = create_item(item_code=item_code, is_stock_item=1, valuation_rate=10)
make_stock_entry(
item_code=item_code,
posting_date=add_days(today(), -3),
posting_time="00:00:00",
purpose="Material Receipt",
qty=10,
to_warehouse="_Test Warehouse - _TC",
do_not_save=True,
)
make_stock_entry(
item_code=item_code,
posting_date=today(),
posting_time="00:00:00",
purpose="Material Receipt",
qty=8,
from_warehouse="_Test Warehouse - _TC",
do_not_save=True,
)
sr_doc = create_stock_reconciliation(
purpose="Stock Reconciliation",
posting_date=add_days(today(), -3),
posting_time="00:00:00",
item_code=item_code,
warehouse="_Test Warehouse - _TC",
valuation_rate=10,
qty=7,
do_not_submit=True,
)
self.assertRaises(frappe.ValidationError, sr_doc.submit)
def make_serialized_item(**args):
args = frappe._dict(args)

View File

@@ -2,7 +2,22 @@
// For license information, please see license.txt
frappe.ui.form.on('Stock Reposting Settings', {
// refresh: function(frm) {
refresh: function(frm) {
frm.trigger('convert_to_item_based_reposting');
},
// }
convert_to_item_based_reposting: function(frm) {
frm.add_custom_button(__('Convert to Item Based Reposting'), function() {
frm.call({
method: 'convert_to_item_wh_reposting',
frezz: true,
doc: frm.doc,
callback: function(r) {
if (!r.exc) {
frm.reload_doc();
}
}
})
})
}
});

View File

@@ -1,6 +1,8 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import add_to_date, get_datetime, get_time_str, time_diff_in_hours
@@ -24,3 +26,62 @@ class StockRepostingSettings(Document):
if diff < 10:
self.end_time = get_time_str(add_to_date(self.start_time, hours=10, as_datetime=True))
@frappe.whitelist()
def convert_to_item_wh_reposting(self):
"""Convert Transaction reposting to Item Warehouse based reposting if Item Based Reposting has enabled."""
reposting_data = get_reposting_entries()
vouchers = [d.voucher_no for d in reposting_data]
item_warehouses = {}
for ledger in get_stock_ledgers(vouchers):
key = (ledger.item_code, ledger.warehouse)
if key not in item_warehouses:
item_warehouses[key] = ledger.posting_date
elif frappe.utils.getdate(item_warehouses.get(key)) > frappe.utils.getdate(ledger.posting_date):
item_warehouses[key] = ledger.posting_date
for key, posting_date in item_warehouses.items():
item_code, warehouse = key
create_repost_item_valuation(item_code, warehouse, posting_date)
for row in reposting_data:
frappe.db.set_value("Repost Item Valuation", row.name, "status", "Skipped")
self.db_set("item_based_reposting", 1)
frappe.msgprint(_("Item Warehouse based reposting has been enabled."))
def get_reposting_entries():
return frappe.get_all(
"Repost Item Valuation",
fields=["voucher_no", "name"],
filters={"status": ("in", ["Queued", "In Progress"]), "docstatus": 1, "based_on": "Transaction"},
)
def get_stock_ledgers(vouchers):
return frappe.get_all(
"Stock Ledger Entry",
fields=["item_code", "warehouse", "posting_date"],
filters={"voucher_no": ("in", vouchers)},
)
def create_repost_item_valuation(item_code, warehouse, posting_date):
frappe.get_doc(
{
"doctype": "Repost Item Valuation",
"company": frappe.get_cached_value("Warehouse", warehouse, "company"),
"posting_date": posting_date,
"based_on": "Item and Warehouse",
"posting_time": "00:00:01",
"item_code": item_code,
"warehouse": warehouse,
"allow_negative_stock": True,
"status": "Queued",
}
).submit()

View File

@@ -88,8 +88,15 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru
update_party_blanket_order(args, out)
# Never try to find a customer price if customer is set in these Doctype
current_customer = args.customer
if args.get("doctype") in ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]:
args.customer = None
out.update(get_price_list_rate(args, item))
args.customer = current_customer
if args.customer and cint(args.is_pos):
out.update(get_pos_profile_item_details(args.company, args, update_data=True))
@@ -146,7 +153,7 @@ def update_stock(args, out):
):
if out.has_batch_no and not args.get("batch_no"):
out.batch_no = get_batch_no(out.item_code, out.warehouse, out.qty)
out.batch_no = get_batch_no(out.item_code, out.warehouse, out.qty).get("batch_no", None)
actual_batch_qty = get_batch_qty(out.batch_no, out.warehouse, out.item_code)
if actual_batch_qty:
out.update(actual_batch_qty)

View File

@@ -62,7 +62,7 @@ def execute(filters=None):
continue
total_stock_value = sum(item_value[(item, item_group)])
row = [item, item_group, total_stock_value]
row = [item, item_map[item]["item_name"], item_group, total_stock_value]
fifo_queue = item_ageing[item]["fifo_queue"]
average_age = 0.00
@@ -89,10 +89,11 @@ def get_columns(filters):
"""return columns"""
columns = [
_("Item") + ":Link/Item:180",
_("Item Group") + "::100",
_("Value") + ":Currency:100",
_("Age") + ":Float:60",
_("Item") + ":Link/Item:150",
_("Item Name") + ":Link/Item:150",
_("Item Group") + "::120",
_("Value") + ":Currency:120",
_("Age") + ":Float:120",
]
return columns
@@ -132,7 +133,7 @@ def get_warehouse_list(filters):
def add_warehouse_column(columns, warehouse_list):
if len(warehouse_list) > 1:
columns += [_("Total Qty") + ":Int:50"]
columns += [_("Total Qty") + ":Int:120"]
for wh in warehouse_list:
columns += [_(wh.name) + ":Int:54"]
columns += [_(wh.name) + ":Int:100"]

View File

@@ -122,7 +122,7 @@ def get_reserved_qty(item_code, warehouse):
and parenttype="Sales Order"
and item_code != parent_item
and exists (select * from `tabSales Order` so
where name = dnpi_in.parent and docstatus = 1 and status != 'Closed')
where name = dnpi_in.parent and docstatus = 1 and status not in ('On Hold', 'Closed'))
) dnpi)
union
(select stock_qty as dnpi_qty, qty as so_item_qty,
@@ -132,7 +132,7 @@ def get_reserved_qty(item_code, warehouse):
and (so_item.delivered_by_supplier is null or so_item.delivered_by_supplier = 0)
and exists(select * from `tabSales Order` so
where so.name = so_item.parent and so.docstatus = 1
and so.status != 'Closed'))
and so.status not in ('On Hold', 'Closed')))
) tab
where
so_item_qty >= so_item_delivered_qty

View File

@@ -1021,7 +1021,7 @@ class update_entries_after(object):
frappe.db.set_value("Bin", bin_name, updated_values)
def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False):
def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_voucher=False):
"""get stock ledger entries filtered by specific posting datetime conditions"""
args["time_format"] = "%H:%i:%s"
@@ -1043,11 +1043,17 @@ def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False):
and warehouse = %(warehouse)s
and is_cancelled = 0
{voucher_condition}
and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
and (
posting_date < %(posting_date)s or
(
posting_date = %(posting_date)s and
time_format(posting_time, %(time_format)s) {operator} time_format(%(posting_time)s, %(time_format)s)
)
)
order by timestamp(posting_date, posting_time) desc, creation desc
limit 1
for update""".format(
voucher_condition=voucher_condition
operator=operator, voucher_condition=voucher_condition
),
args,
as_dict=1,
@@ -1144,7 +1150,7 @@ def get_stock_ledger_entries(
def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None):
return frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_detail_no": voucher_detail_no, "name": ["!=", excluded_sle]},
{"voucher_detail_no": voucher_detail_no, "name": ["!=", excluded_sle], "is_cancelled": 0},
[
"item_code",
"warehouse",
@@ -1285,7 +1291,7 @@ def get_stock_reco_qty_shift(args):
stock_reco_qty_shift = flt(args.actual_qty)
else:
# reco is being submitted
last_balance = get_previous_sle_of_current_voucher(args, exclude_current_voucher=True).get(
last_balance = get_previous_sle_of_current_voucher(args, "<=", exclude_current_voucher=True).get(
"qty_after_transaction"
)

View File

@@ -0,0 +1,40 @@
import json
import frappe
from frappe.test_runner import make_test_records
from frappe.tests.utils import FrappeTestCase
from erpnext.stock.get_item_details import get_item_details
test_ignore = ["BOM"]
test_dependencies = ["Customer", "Supplier", "Item", "Price List", "Item Price"]
class TestGetItemDetail(FrappeTestCase):
def setUp(self):
make_test_records("Price List")
super().setUp()
def test_get_item_detail_purchase_order(self):
args = frappe._dict(
{
"item_code": "_Test Item",
"company": "_Test Company",
"customer": "_Test Customer",
"conversion_rate": 1.0,
"price_list_currency": "USD",
"plc_conversion_rate": 1.0,
"doctype": "Purchase Order",
"name": None,
"supplier": "_Test Supplier",
"transaction_date": None,
"conversion_rate": 1.0,
"price_list": "_Test Buying Price List",
"is_subcontracted": 0,
"ignore_pricing_rule": 1,
"qty": 1,
}
)
details = get_item_details(args)
self.assertEqual(details.get("price_list_rate"), 100)

View File

@@ -4047,7 +4047,7 @@ Server Error,Serverfehler,
Service Level Agreement has been changed to {0}.,Service Level Agreement wurde in {0} geändert.,
Service Level Agreement was reset.,Service Level Agreement wurde zurückgesetzt.,
Service Level Agreement with Entity Type {0} and Entity {1} already exists.,Service Level Agreement mit Entitätstyp {0} und Entität {1} ist bereits vorhanden.,
Set,Menge,
Set Loyalty Program,Treueprogramm eintragen,
Set Meta Tags,Festlegen von Meta-Tags,
Set {0} in company {1},{0} in Firma {1} festlegen,
Setup,Einstellungen,
@@ -4227,10 +4227,8 @@ To date cannot be before From date,Bis-Datum kann nicht vor Von-Datum liegen,
Write Off,Abschreiben,
{0} Created,{0} Erstellt,
Email Id,E-Mail-ID,
No,Kein,
Reference Doctype,Referenz-DocType,
User Id,Benutzeridentifikation,
Yes,Ja,
Actual ,Tatsächlich,
Add to cart,In den Warenkorb legen,
Budget,Budget,
@@ -9899,3 +9897,5 @@ Total Asset,Aktiva,
Total Liability,Verbindlichkeiten,
Total Equity,Eigenkapital,
Warehouse wise Stock Value,Warenwert nach Lager,
Discount Validity,Frist für den Rabatt,
Discount Validity Based On,Frist für den Rabatt berechnet sich nach,
Can't render this file because it is too large.

View File

@@ -51,36 +51,31 @@ def get_tabs(categories):
return tab_values
def get_category_records(categories):
def get_category_records(categories: list):
categorical_data = {}
for category in categories:
if category == "item_group":
categorical_data["item_group"] = frappe.db.sql(
"""
Select
name, parent_item_group, is_group, image, route
from
`tabItem Group`
where
parent_item_group = 'All Item Groups'
and show_in_website = 1
""",
as_dict=1,
for c in categories:
if c == "item_group":
categorical_data["item_group"] = frappe.db.get_all(
"Item Group",
filters={"parent_item_group": "All Item Groups", "show_in_website": 1},
fields=["name", "parent_item_group", "is_group", "image", "route"],
)
else:
doctype = frappe.unscrub(category)
fields = ["name"]
if frappe.get_meta(doctype, cached=True).get_field("image"):
continue
doctype = frappe.unscrub(c)
fields = ["name"]
try:
meta = frappe.get_meta(doctype, cached=True)
if meta.get_field("image"):
fields += ["image"]
categorical_data[category] = frappe.db.sql(
f"""
Select
{",".join(fields)}
from
`tab{doctype}`
""",
as_dict=1,
)
data = frappe.db.get_all(doctype, fields=fields)
categorical_data[c] = data
except BaseException:
frappe.throw(_("DocType {} not found").format(doctype))
continue
return categorical_data