Compare commits

...

79 Commits

Author SHA1 Message Date
Frappe PR Bot
5778f227ee chore(release): Bumped to Version 14.0.2
## [14.0.2](https://github.com/frappe/erpnext/compare/v14.0.1...v14.0.2) (2022-08-23)

### Bug Fixes

* additional-cost in items table ([da69cc5](da69cc5477))
* base_amount and exchange_rate in additional-cost table ([d48487a](d48487ada2))
* Cash and non trade discount calculation ([b6d2de2](b6d2de2cc1))
* don't allow to create SCR directly (backport [#31924](https://github.com/frappe/erpnext/issues/31924)) ([#31926](https://github.com/frappe/erpnext/issues/31926)) ([0bfb774](0bfb774bdf))
* incorrect buying amount in Gross Profit rpt ([981add9](981add9b6f))
* incorrect tax amt due to different exchange rate in PR and PI ([a767326](a76732613e))
* limited options for no-of-employees in crm ([735a608](735a60807a))
* make rate field read-only in subcontracting receipt item (backport [#31905](https://github.com/frappe/erpnext/issues/31905)) ([#31906](https://github.com/frappe/erpnext/issues/31906)) ([6be77d5](6be77d5729))
* map old data as per new options of no-of-employees ([35e9bfc](35e9bfca38))
* **pos:** edge case while closing pos ([#31892](https://github.com/frappe/erpnext/issues/31892)) ([200a971](200a971743))
* process loan interest accrual ([66e5202](66e5202642))
* recalculate rate of items based on "Recalculate Rate" checkbox ([9e60dd3](9e60dd32e8))
* TDS calculation for advance payment ([78b39d6](78b39d6ca4))
* term loan interest calculation ([8fdbbf3](8fdbbf374d))
* test "test_pending_and_received_qty" ([e099e10](e099e10c8e))
* Test cases ([3afb625](3afb625ff8))
* Unable to make payment entry against Fees using education app ([1aa96de](1aa96defda))

### Performance Improvements

* use `create_custom_fields` ([#31853](https://github.com/frappe/erpnext/issues/31853)) ([6656d23](6656d23e45))
2022-08-23 05:28:37 +00:00
Deepesh Garg
7ac75aab1a Merge pull request #31932 from frappe/version-14-hotfix
chore: weekly version 14 release
2022-08-23 10:57:10 +05:30
Deepesh Garg
e831b6e054 Merge pull request #31930 from frappe/mergify/bp/version-14-hotfix/pr-31910
fix: Cash and non trade discount calculation (backport #31910)
2022-08-23 10:36:15 +05:30
Deepesh Garg
856a64b77c chore: Linting issues
(cherry picked from commit 1cb7ae16ab)
2022-08-23 04:49:57 +00:00
Deepesh Garg
3afb625ff8 fix: Test cases
(cherry picked from commit ae3dce0cbd)
2022-08-23 04:49:57 +00:00
Deepesh Garg
b6d2de2cc1 fix: Cash and non trade discount calculation
(cherry picked from commit 3b15966cc9)
2022-08-23 04:49:57 +00:00
mergify[bot]
0bfb774bdf fix: don't allow to create SCR directly (backport #31924) (#31926)
fix: don't allow to create SCR directly (#31924)

(cherry picked from commit bf5c43322a)

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-22 18:38:21 +05:30
Deepesh Garg
d7a8db04a1 Merge pull request #31916 from frappe/mergify/bp/version-14-hotfix/pr-31856
fix: incorrect tax amt due to different exchange rate in PR and PI (backport #31856)
2022-08-22 10:57:53 +05:30
Deepesh Garg
2d5ae811d2 Merge pull request #31917 from frappe/mergify/bp/version-14-hotfix/pr-31871
fix: incorrect buying amount in Gross Profit rpt (backport #31871)
2022-08-22 10:56:47 +05:30
Deepesh Garg
be9607e27b Merge pull request #31915 from frappe/mergify/bp/version-14-hotfix/pr-31894
fix: TDS calculation for advance payment (backport #31894)
2022-08-22 10:32:04 +05:30
Deepesh Garg
fd45a7afbe Merge pull request #31818 from frappe/mergify/bp/version-14-hotfix/pr-31816
fix: limited options for no-of-employees in the crm documents (backport #31816)
2022-08-22 10:31:06 +05:30
Deepesh Garg
0c73af6ee2 Merge pull request #31809 from frappe/mergify/bp/version-14-hotfix/pr-31801
Tds report (backport #31801)
2022-08-22 10:30:42 +05:30
ruthra kumar
981add9b6f fix: incorrect buying amount in Gross Profit rpt
(cherry picked from commit 967dd398e7)
2022-08-22 05:00:26 +00:00
Deepesh Garg
8f5736c500 Merge pull request #31832 from frappe/mergify/bp/version-14-hotfix/pr-31830
fix: Unable to make payment entry against Fees using education app (backport #31830)
2022-08-22 10:29:26 +05:30
Deepesh Garg
052f7c3345 Merge pull request #31827 from frappe/mergify/bp/version-14-hotfix/pr-31799
fix: process loan interest accrual (backport #31799)
2022-08-22 10:28:50 +05:30
Deepesh Garg
fa4a40812c Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-31816 2022-08-22 09:09:16 +05:30
ruthra kumar
a76732613e fix: incorrect tax amt due to different exchange rate in PR and PI
(cherry picked from commit 5fd0770372)
2022-08-22 03:38:43 +00:00
Deepesh Garg
dd602989a8 Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-31801 2022-08-22 09:00:36 +05:30
Maharshi Patel
78b39d6ca4 fix: TDS calculation for advance payment
"against_voucher": ["is", "not set"] was used in query due to which if TDS was added on "advance" payment vouchers and then reconciled against purchase invoice. it will not find those vouchers and consider this as first-time threshold due to which it will calculate Tax for all transactions.

(cherry picked from commit a452143782)
2022-08-22 03:30:04 +00:00
Deepesh Garg
77fa64e100 Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-31799 2022-08-22 08:59:57 +05:30
Deepesh Garg
ac04fc60ef Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-31830 2022-08-22 08:59:31 +05:30
mergify[bot]
6be77d5729 fix: make rate field read-only in subcontracting receipt item (backport #31905) (#31906)
fix: make rate field read-only in subcontracting receipt item (#31905)

(cherry picked from commit 588ca68171)

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-20 19:28:21 +05:30
Sagar Sharma
37e5b93e2d Merge pull request #31900 from frappe/mergify/bp/version-14-hotfix/pr-31899
fix: repost item valuation for subcontracting receipt  (backport #31899)
2022-08-19 22:41:28 +05:30
Sagar Sharma
19d29d1861 chore: add option for "Subcontracting Receipt" in "Voucher Type"
(cherry picked from commit f92f3e0208)
2022-08-19 16:31:53 +00:00
Sagar Sharma
0db912998a chore: allow subcontracting receipt backdated entry
(cherry picked from commit f8c11847bb)
2022-08-19 16:31:53 +00:00
mergify[bot]
200a971743 fix(pos): edge case while closing pos (#31892) 2022-08-19 17:11:13 +05:30
Sagar Sharma
c0f0986539 Merge pull request #31897 from frappe/mergify/bp/version-14-hotfix/pr-31890
fix: additional-costs in SCO and SCR (backport #31890)
2022-08-19 15:58:06 +05:30
Sagar Sharma
376293326b chore: conflicts 2022-08-19 15:30:50 +05:30
Sagar Sharma
9a29e3c9f2 chore: add test for additional-cost
(cherry picked from commit c247cf728c)
2022-08-19 09:56:52 +00:00
Sagar Sharma
e099e10c8e fix: test "test_pending_and_received_qty"
(cherry picked from commit addd7347d8)
2022-08-19 09:56:52 +00:00
Sagar Sharma
3b222339b8 chore: add additional-cost table in SCR
(cherry picked from commit 256b4245d5)

# Conflicts:
#	erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
2022-08-19 09:56:51 +00:00
Sagar Sharma
9e60dd32e8 fix: recalculate rate of items based on "Recalculate Rate" checkbox
(cherry picked from commit 2fc6833684)
2022-08-19 09:56:51 +00:00
Sagar Sharma
7ff5414571 chore: move "set_missing_values_in_additional_costs" from SCO to SC"
(cherry picked from commit ea82fe5bc2)
2022-08-19 09:56:50 +00:00
Sagar Sharma
d48487ada2 fix: base_amount and exchange_rate in additional-cost table
(cherry picked from commit eabd3135f0)
2022-08-19 09:56:50 +00:00
Sagar Sharma
da69cc5477 fix: additional-cost in items table
(cherry picked from commit d7ed4093d8)
2022-08-19 09:56:50 +00:00
Sagar Vora
b2a720d847 Merge pull request #31891 from frappe/mergify/bp/version-14-hotfix/pr-31853
perf: use `create_custom_fields` (backport #31853)
2022-08-18 15:57:59 +00:00
Sagar Vora
6656d23e45 perf: use create_custom_fields (#31853)
* perf: use `create_custom_fields`

* fix: default must be a string

(cherry picked from commit aafb735283)
2022-08-18 15:31:42 +00:00
mergify[bot]
ae5c05081d chore: remove unwanted field "provisional_expense_account" from SCR (backport #31847) (#31886)
chore: remove unwanted field "provisional_expense_account" from SCR (#31847)

(cherry picked from commit 7e88eb549f)

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-18 17:41:01 +05:30
Frappe PR Bot
028e939cca chore(release): Bumped to Version 14.0.1
## [14.0.1](https://github.com/frappe/erpnext/compare/v14.0.0...v14.0.1) (2022-08-18)

### Bug Fixes

* add asset repair to accounting dimension list ([65bb1d8](65bb1d8cc2))
* Add dimension section in subcontracting doctypes (backport [#31849](https://github.com/frappe/erpnext/issues/31849)) ([#31877](https://github.com/frappe/erpnext/issues/31877)) ([50ad612](50ad612453))
* check item_code in all rows of po_items (backport [#31741](https://github.com/frappe/erpnext/issues/31741)) ([#31842](https://github.com/frappe/erpnext/issues/31842)) ([82f1dd2](82f1dd268d))
* contact search in request for quotation (backport [#31828](https://github.com/frappe/erpnext/issues/31828)) ([#31840](https://github.com/frappe/erpnext/issues/31840)) ([74664a3](74664a34c0))
* delete custom fields on deletion of inventory dimension ([354a9d6](354a9d6169))
* **ecommerce:** remove query to non-existing field (backport [#31771](https://github.com/frappe/erpnext/issues/31771)) ([#31773](https://github.com/frappe/erpnext/issues/31773)) ([8737c10](8737c10ce4))
* getting error to show sales invoice group or print rep… (backport [#31756](https://github.com/frappe/erpnext/issues/31756)) ([#31767](https://github.com/frappe/erpnext/issues/31767)) ([53b9d61](53b9d61c46))
* incorrect produced-qty in production-plan-item (backport [#31706](https://github.com/frappe/erpnext/issues/31706)) ([#31861](https://github.com/frappe/erpnext/issues/31861)) ([010a0ca](010a0ca0a9))
* incorrect rate in BOM exploded items (backport [#31513](https://github.com/frappe/erpnext/issues/31513)) ([#31864](https://github.com/frappe/erpnext/issues/31864)) ([abe1894](abe18945a6))
* intercompany SO throws exception ([1d1f12f](1d1f12f949))
* linter (backport [#31763](https://github.com/frappe/erpnext/issues/31763)) ([#31766](https://github.com/frappe/erpnext/issues/31766)) ([3092131](3092131913))
* Make expense account editable in Purchase Receipt Item (backport [#31730](https://github.com/frappe/erpnext/issues/31730)) ([#31878](https://github.com/frappe/erpnext/issues/31878)) ([b637d4d](b637d4d5f1))
* Make expense account editable in Subcontracting Receipt Item (backport [#31848](https://github.com/frappe/erpnext/issues/31848)) ([#31870](https://github.com/frappe/erpnext/issues/31870)) ([aa5aaa1](aa5aaa113e))
* not able to issue expired batches ([ce5fc5b](ce5fc5b457))
* posting_date of linked vouchers should not affect outstanding ([0faa7b0](0faa7b0432))
* **projects:** Add missing comma ([623f56a](623f56a95c))
* set `billing_address` for purchases in `get_party_details` ([5a28ba8](5a28ba8537))
* set `company_address` for purchases in `party.js` ([4c82533](4c82533239))
* specify allowed doctype in queries (backport [#31761](https://github.com/frappe/erpnext/issues/31761)) ([#31764](https://github.com/frappe/erpnext/issues/31764)) ([6dce122](6dce122825))
* Transit filter for Default Target Warehouse in SE (backport [#31839](https://github.com/frappe/erpnext/issues/31839)) ([#31873](https://github.com/frappe/erpnext/issues/31873)) ([04d3571](04d3571dd9))
2022-08-18 08:30:58 +00:00
Deepesh Garg
216cb9b07b Merge pull request #31881 from frappe/version-14-hotfix
chore: weekly version-14 release
2022-08-18 13:59:28 +05:30
mergify[bot]
50ad612453 fix: Add dimension section in subcontracting doctypes (backport #31849) (#31877)
* fix: Add dimension section in subcontracting doctypes (#31849)

(cherry picked from commit 8704ca783d)

# Conflicts:
#	erpnext/patches.txt

* chore: conflicts

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-18 12:04:07 +05:30
Deepesh Garg
6b71af9008 Merge pull request #31883 from frappe/mergify/bp/version-14-hotfix/pr-31875
fix(projects): Add missing comma (backport #31875)
2022-08-18 11:20:55 +05:30
Deepesh Garg
3c8412efdb Merge pull request #31796 from frappe/mergify/bp/version-14-hotfix/pr-31737
fix: incorrect invoice outstanding in reconciliation tool (backport #31737)
2022-08-18 11:20:26 +05:30
Aditya Hase
623f56a95c fix(projects): Add missing comma
Added with https://github.com/frappe/erpnext/pull/31360

(cherry picked from commit d38778e400)
2022-08-18 05:49:58 +00:00
mergify[bot]
b637d4d5f1 fix: Make expense account editable in Purchase Receipt Item (backport #31730) (#31878)
fix: Make expense account editable in Purchase Receipt Item (#31730)

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
(cherry picked from commit 1a6508972e)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2022-08-18 11:16:15 +05:30
rohitwaghchaure
601bc64618 Merge pull request #31869 from frappe/mergify/bp/version-14-hotfix/pr-31860
fix: delete custom fields on deletion of inventory dimension (backport #31860)
2022-08-17 21:45:57 +05:30
mergify[bot]
04d3571dd9 fix: Transit filter for Default Target Warehouse in SE (backport #31839) (#31873)
fix: Transit filter for Default Target Warehouse in SE (#31839)

(cherry picked from commit f1a612245c)

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-17 17:26:15 +05:30
mergify[bot]
aa5aaa113e fix: Make expense account editable in Subcontracting Receipt Item (backport #31848) (#31870)
fix: Make expense account editable in Subcontracting Receipt Item (#31848)

(cherry picked from commit 2d04e71412)

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-17 16:31:18 +05:30
Rohit Waghchaure
354a9d6169 fix: delete custom fields on deletion of inventory dimension
(cherry picked from commit 0b39a0123e)
2022-08-17 10:14:14 +00:00
rohitwaghchaure
c7e2217c92 Merge pull request #31867 from frappe/mergify/bp/version-14-hotfix/pr-31863
fix: not able to issue expired batches (backport #31863)
2022-08-17 15:43:32 +05:30
Rohit Waghchaure
ce5fc5b457 fix: not able to issue expired batches
(cherry picked from commit 795c94384a)
2022-08-17 09:16:57 +00:00
mergify[bot]
abe18945a6 fix: incorrect rate in BOM exploded items (backport #31513) (#31864)
fix: incorrect rate in BOM exploded items (#31513)

(cherry picked from commit 313625c349)

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-17 14:37:40 +05:30
mergify[bot]
010a0ca0a9 fix: incorrect produced-qty in production-plan-item (backport #31706) (#31861)
fix: incorrect produced-qty in production-plan-item (#31706)

(cherry picked from commit 538cd6fdcf)

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-17 13:46:33 +05:30
mergify[bot]
74664a34c0 fix: contact search in request for quotation (backport #31828) (#31840)
fix: contact search in request for quotation (#31828)

(cherry picked from commit e5e88bb9f1)

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-15 12:19:59 +05:30
mergify[bot]
82f1dd268d fix: check item_code in all rows of po_items (backport #31741) (#31842)
fix: check item_code in all rows of po_items (#31741)

fix: check the item code in each row of PO items
(cherry picked from commit 0047e18a9b)

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-14 16:00:54 +05:30
Deepesh Garg
1aa96defda fix: Unable to make payment entry against Fees using education app
(cherry picked from commit 79ac50d0f7)
2022-08-11 15:17:42 +00:00
Abhinav Raut
8fdbbf374d fix: term loan interest calculation
(cherry picked from commit 534d7ce64b)
2022-08-11 10:18:24 +00:00
Abhinav Raut
66e5202642 fix: process loan interest accrual
(cherry picked from commit 9ef8d5c5c3)
2022-08-11 10:18:24 +00:00
Nabin Hait
35e9bfca38 fix: map old data as per new options of no-of-employees
(cherry picked from commit 909945c0ac)
2022-08-09 15:18:25 +00:00
Nabin Hait
735a60807a fix: limited options for no-of-employees in crm
(cherry picked from commit 7ecd67605f)
2022-08-09 15:18:25 +00:00
mergify[bot]
c3fd802351 refactor: use browser native lazy loading (backport #31814) (#31815)
refactor: use browser native lazy loading (#31814)

(cherry picked from commit 08d7c48dc7)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-08-09 18:51:27 +05:30
Deepesh Garg
6046f8bc5e Merge pull request #31798 from frappe/mergify/bp/version-14-hotfix/pr-31779
Bug add accouting dimension in asset repair (backport #31779)
2022-08-09 17:58:35 +05:30
Akash Krishna
d7db8ed12e Tds report (#31801)
* fix: TDS Computation Summary Report not loading, too many values to unpack

(cherry picked from commit 32b30bc5de)
2022-08-09 12:12:33 +00:00
Sagar Vora
adcd21724b Merge pull request #31793 from frappe/mergify/bp/version-14-hotfix/pr-31733
fix: set `billing_address` for purchases in `get_party_details` (backport #31733)
2022-08-08 12:09:43 +00:00
ruthra kumar
04bdff736b chore: patch for creating existing dimensions in asset repair
(cherry picked from commit 80f508c4b1)
2022-08-08 11:10:25 +00:00
ruthra kumar
65bb1d8cc2 fix: add asset repair to accounting dimension list
(cherry picked from commit 452584c4bd)
2022-08-08 11:10:25 +00:00
Deepesh Garg
ebf766cf62 Merge pull request #31794 from frappe/mergify/bp/version-14-hotfix/pr-31777
fix: intercompany SO created from Purchase Order throws exception (backport #31777)
2022-08-08 16:36:46 +05:30
ruthra kumar
80bf47170f test: posting_date should not affect outstanding amount calculation
(cherry picked from commit ef312b8fc4)
2022-08-08 10:59:05 +00:00
ruthra kumar
0faa7b0432 fix: posting_date of linked vouchers should not affect outstanding
posting_date filter should not be applied for linked vouchers.

(cherry picked from commit 5f1562c5b2)
2022-08-08 10:59:05 +00:00
ruthra kumar
1d1f12f949 fix: intercompany SO throws exception
(cherry picked from commit af0a353b79)
2022-08-08 10:36:55 +00:00
Sagar Vora
4c82533239 fix: set company_address for purchases in party.js
(cherry picked from commit d05082987f)
2022-08-08 07:31:44 +00:00
Sagar Vora
5a28ba8537 fix: set billing_address for purchases in get_party_details
(cherry picked from commit a3625b3817)
2022-08-08 07:31:44 +00:00
mergify[bot]
8737c10ce4 fix(ecommerce): remove query to non-existing field (backport #31771) (#31773)
fix(ecommerce): remove query to non-existing field (#31771)

(cherry picked from commit 17b9bfd249)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-08-03 16:49:16 +05:30
Ankush Menat
2defb89962 ci: fix automated release regex (#31770)
[skip ci]
2022-08-03 16:15:32 +05:30
mergify[bot]
53b9d61c46 fix: getting error to show sales invoice group or print rep… (backport #31756) (#31767)
fix: getting error to show sales invoice group or print rep… (#31756)

fix: formatter getting error to show sales invoice group or print report.

1 - When I view the Gross Profit report in Sales Invoice mode, the table is all broken.
Error on browser console:
TypeError: Cannot read properties of undefined (reading 'indent')

2 - When I try to print, no matter the Group (Sales Invoice, Item Code, Item Group...) nothing happens. in browser log console I have the following error:
TypeError: Cannot read properties of undefined (reading 'content')

i fixed both errors and all working perfectly.

(cherry picked from commit ea88451875)

Co-authored-by: HarryPaulo <paulo_fabris@hotmail.com>
2022-08-03 11:22:54 +05:30
mergify[bot]
3092131913 fix: linter (backport #31763) (#31766)
fix: linter (#31763)

(cherry picked from commit 9c580dde39)

Co-authored-by: Devin Slauenwhite <devin.slauenwhite@gmail.com>
2022-08-03 11:17:44 +05:30
mergify[bot]
6dce122825 fix: specify allowed doctype in queries (backport #31761) (#31764)
fix: specify allowed doctype in queries (#31761)

(cherry picked from commit 9baa222976)

Co-authored-by: Sagar Vora <sagar@resilient.tech>
2022-08-03 11:16:41 +05:30
Ankush Menat
248cc48842 ci: setup releases for v14 (#31759)
[skip ci]
2022-08-02 12:11:36 +05:30
Deepesh Garg
ebd8f2f45b chore: verison bump and change log 2022-08-01 22:29:34 +05:30
77 changed files with 1135 additions and 382 deletions

View File

@@ -2,7 +2,7 @@ name: Generate Semantic Release
on:
push:
branches:
- version-13
- version-14
jobs:
release:
name: Release
@@ -13,10 +13,12 @@ jobs:
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js v14
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 14
node-version: 16
- name: Setup dependencies
run: |
npm install @semantic-release/git @semantic-release/exec --no-save
@@ -28,4 +30,4 @@ jobs:
GIT_AUTHOR_EMAIL: "developers@frappe.io"
GIT_COMMITTER_NAME: "Frappe PR Bot"
GIT_COMMITTER_EMAIL: "developers@frappe.io"
run: npx semantic-release
run: npx semantic-release

View File

@@ -1,5 +1,5 @@
{
"branches": ["version-13"],
"branches": ["version-14"],
"plugins": [
"@semantic-release/commit-analyzer", {
"preset": "angular",
@@ -10,7 +10,7 @@
"@semantic-release/release-notes-generator",
[
"@semantic-release/exec", {
"prepareCmd": 'sed -ir "s/[0-9]*\.[0-9]*\.[0-9]*/${nextRelease.version}/" erpnext/__init__.py'
"prepareCmd": 'sed -ir -E "s/\"[0-9]+\.[0-9]+\.[0-9]+\"/\"${nextRelease.version}\"/" erpnext/__init__.py'
}
],
[
@@ -21,4 +21,4 @@
],
"@semantic-release/github"
]
}
}

View File

@@ -2,7 +2,7 @@ import inspect
import frappe
__version__ = "14.0.0-dev"
__version__ = "14.0.2"
def get_default_company(user=None):

View File

@@ -366,7 +366,7 @@ def update_outstanding_amt(
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
# Didn't use db_set for optimisation purpose
# Didn't use db_set for optimization purpose
ref_doc.outstanding_amount = bal
frappe.db.set_value(against_voucher_type, against_voucher, "outstanding_amount", bal)

View File

@@ -181,7 +181,11 @@ class PaymentEntry(AccountsController):
frappe.throw(_("Party is mandatory"))
_party_name = "title" if self.party_type == "Shareholder" else self.party_type.lower() + "_name"
self.party_name = frappe.db.get_value(self.party_type, self.party, _party_name)
if frappe.db.has_column(self.party_type, _party_name):
self.party_name = frappe.db.get_value(self.party_type, self.party, _party_name)
else:
self.party_name = frappe.db.get_value(self.party_type, self.party, "name")
if self.party:
if not self.party_balance:
@@ -295,6 +299,9 @@ class PaymentEntry(AccountsController):
def validate_reference_documents(self):
valid_reference_doctypes = self.get_valid_reference_doctypes()
if not valid_reference_doctypes:
return
for d in self.get("references"):
if not d.allocated_amount:
continue
@@ -362,7 +369,7 @@ class PaymentEntry(AccountsController):
if not d.allocated_amount:
continue
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"):
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
outstanding_amount, is_return = frappe.get_cached_value(
d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"]
)
@@ -1184,6 +1191,7 @@ def get_outstanding_reference_documents(args):
ple = qb.DocType("Payment Ledger Entry")
common_filter = []
posting_and_due_date = []
# confirm that Supplier is not blocked
if args.get("party_type") == "Supplier":
@@ -1200,7 +1208,7 @@ def get_outstanding_reference_documents(args):
party_account_currency = get_account_currency(args.get("party_account"))
company_currency = frappe.get_cached_value("Company", args.get("company"), "default_currency")
# Get positive outstanding sales /purchase invoices/ Fees
# Get positive outstanding sales /purchase invoices
condition = ""
if args.get("voucher_type") and args.get("voucher_no"):
condition = " and voucher_type={0} and voucher_no={1}".format(
@@ -1224,7 +1232,7 @@ def get_outstanding_reference_documents(args):
condition += " and {0} between '{1}' and '{2}'".format(
fieldname, args.get(date_fields[0]), args.get(date_fields[1])
)
common_filter.append(ple[fieldname][args.get(date_fields[0]) : args.get(date_fields[1])])
posting_and_due_date.append(ple[fieldname][args.get(date_fields[0]) : args.get(date_fields[1])])
if args.get("company"):
condition += " and company = {0}".format(frappe.db.escape(args.get("company")))
@@ -1235,6 +1243,7 @@ def get_outstanding_reference_documents(args):
args.get("party"),
args.get("party_account"),
common_filter=common_filter,
posting_date=posting_and_due_date,
min_outstanding=args.get("outstanding_amt_greater_than"),
max_outstanding=args.get("outstanding_amt_less_than"),
)
@@ -1595,10 +1604,11 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
elif reference_doctype != "Journal Entry":
if not total_amount:
if party_account_currency == company_currency:
total_amount = ref_doc.base_grand_total
# for handling cases that don't have multi-currency (base field)
total_amount = ref_doc.get("grand_total") or ref_doc.get("base_grand_total")
exchange_rate = 1
else:
total_amount = ref_doc.grand_total
total_amount = ref_doc.get("grand_total")
if not exchange_rate:
# Get the exchange rate from the original ref doc
# or get it based on the posting date of the ref doc.
@@ -1609,7 +1619,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
outstanding_amount = ref_doc.get("outstanding_amount")
else:
outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
outstanding_amount = flt(total_amount) - flt(ref_doc.get("advance_paid"))
else:
# Get the exchange rate based on the posting date of the ref doc.
@@ -1627,16 +1637,23 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
@frappe.whitelist()
def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None):
def get_payment_entry(
dt, dn, party_amount=None, bank_account=None, bank_amount=None, party_type=None, payment_type=None
):
reference_doc = None
doc = frappe.get_doc(dt, dn)
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0:
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
party_type = set_party_type(dt)
if not party_type:
party_type = set_party_type(dt)
party_account = set_party_account(dt, dn, doc, party_type)
party_account_currency = set_party_account_currency(dt, party_account, doc)
payment_type = set_payment_type(dt, doc)
if not payment_type:
payment_type = set_payment_type(dt, doc)
grand_total, outstanding_amount = set_grand_total_and_outstanding_amount(
party_amount, dt, party_account_currency, doc
)
@@ -1786,8 +1803,6 @@ def set_party_account(dt, dn, doc, party_type):
party_account = get_party_account_based_on_invoice_discounting(dn) or doc.debit_to
elif dt == "Purchase Invoice":
party_account = doc.credit_to
elif dt == "Fees":
party_account = doc.receivable_account
else:
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
return party_account
@@ -1803,8 +1818,7 @@ def set_party_account_currency(dt, party_account, doc):
def set_payment_type(dt, doc):
if (
dt == "Sales Order"
or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)
dt == "Sales Order" or (dt in ("Sales Invoice", "Dunning") and doc.outstanding_amount > 0)
) or (dt == "Purchase Invoice" and doc.outstanding_amount < 0):
payment_type = "Receive"
else:
@@ -1822,18 +1836,15 @@ def set_grand_total_and_outstanding_amount(party_amount, dt, party_account_curre
else:
grand_total = doc.rounded_total or doc.grand_total
outstanding_amount = doc.outstanding_amount
elif dt == "Fees":
grand_total = doc.grand_total
outstanding_amount = doc.outstanding_amount
elif dt == "Dunning":
grand_total = doc.grand_total
outstanding_amount = doc.grand_total
else:
if party_account_currency == doc.company_currency:
grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total)
grand_total = flt(doc.get("base_rounded_total") or doc.get("base_grand_total"))
else:
grand_total = flt(doc.get("rounded_total") or doc.grand_total)
outstanding_amount = grand_total - flt(doc.advance_paid)
grand_total = flt(doc.get("rounded_total") or doc.get("grand_total"))
outstanding_amount = doc.get("outstanding_amount") or (grand_total - flt(doc.advance_paid))
return grand_total, outstanding_amount

View File

@@ -22,6 +22,7 @@ class PaymentReconciliation(Document):
def __init__(self, *args, **kwargs):
super(PaymentReconciliation, self).__init__(*args, **kwargs)
self.common_filter_conditions = []
self.ple_posting_date_filter = []
@frappe.whitelist()
def get_unreconciled_entries(self):
@@ -150,6 +151,7 @@ class PaymentReconciliation(Document):
return_outstanding = ple_query.get_voucher_outstandings(
vouchers=return_invoices,
common_filter=self.common_filter_conditions,
posting_date=self.ple_posting_date_filter,
min_outstanding=-(self.minimum_payment_amount) if self.minimum_payment_amount else None,
max_outstanding=-(self.maximum_payment_amount) if self.maximum_payment_amount else None,
get_payments=True,
@@ -187,6 +189,7 @@ class PaymentReconciliation(Document):
self.party,
self.receivable_payable_account,
common_filter=self.common_filter_conditions,
posting_date=self.ple_posting_date_filter,
min_outstanding=self.minimum_invoice_amount if self.minimum_invoice_amount else None,
max_outstanding=self.maximum_invoice_amount if self.maximum_invoice_amount else None,
)
@@ -350,6 +353,7 @@ class PaymentReconciliation(Document):
def build_qb_filter_conditions(self, get_invoices=False, get_return_invoices=False):
self.common_filter_conditions.clear()
self.ple_posting_date_filter.clear()
ple = qb.DocType("Payment Ledger Entry")
self.common_filter_conditions.append(ple.company == self.company)
@@ -359,15 +363,15 @@ class PaymentReconciliation(Document):
if get_invoices:
if self.from_invoice_date:
self.common_filter_conditions.append(ple.posting_date.gte(self.from_invoice_date))
self.ple_posting_date_filter.append(ple.posting_date.gte(self.from_invoice_date))
if self.to_invoice_date:
self.common_filter_conditions.append(ple.posting_date.lte(self.to_invoice_date))
self.ple_posting_date_filter.append(ple.posting_date.lte(self.to_invoice_date))
elif get_return_invoices:
if self.from_payment_date:
self.common_filter_conditions.append(ple.posting_date.gte(self.from_payment_date))
self.ple_posting_date_filter.append(ple.posting_date.gte(self.from_payment_date))
if self.to_payment_date:
self.common_filter_conditions.append(ple.posting_date.lte(self.to_payment_date))
self.ple_posting_date_filter.append(ple.posting_date.lte(self.to_payment_date))
def get_conditions(self, get_payments=False):
condition = " and company = '{0}' ".format(self.company)

View File

@@ -283,6 +283,41 @@ class TestPaymentReconciliation(FrappeTestCase):
self.assertEqual(len(pr.get("invoices")), 2)
self.assertEqual(len(pr.get("payments")), 2)
def test_filter_posting_date_case2(self):
"""
Posting date should not affect outstanding amount calculation
"""
from_date = add_days(nowdate(), -30)
to_date = nowdate()
self.create_payment_entry(amount=25, posting_date=from_date).submit()
self.create_sales_invoice(rate=25, qty=1, posting_date=to_date)
pr = self.create_payment_reconciliation()
pr.from_invoice_date = pr.from_payment_date = from_date
pr.to_invoice_date = pr.to_payment_date = to_date
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 1)
invoices = [x.as_dict() for x in pr.invoices]
payments = [x.as_dict() for x in pr.payments]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.reconcile()
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 0)
self.assertEqual(len(pr.payments), 0)
pr.from_invoice_date = pr.from_payment_date = to_date
pr.to_invoice_date = pr.to_payment_date = to_date
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 0)
def test_filter_invoice_limit(self):
# check filter condition - invoice limit
transaction_date = nowdate()

View File

@@ -36,6 +36,15 @@ frappe.ui.form.on('POS Closing Entry', {
});
set_html_data(frm);
if (frm.doc.docstatus == 1) {
if (!frm.doc.posting_date) {
frm.set_value("posting_date", frappe.datetime.nowdate());
}
if (!frm.doc.posting_time) {
frm.set_value("posting_time", frappe.datetime.now_time());
}
}
},
refresh: function(frm) {

View File

@@ -11,6 +11,7 @@
"period_end_date",
"column_break_3",
"posting_date",
"posting_time",
"pos_opening_entry",
"status",
"section_break_5",
@@ -51,7 +52,6 @@
"fieldtype": "Datetime",
"in_list_view": 1,
"label": "Period End Date",
"read_only": 1,
"reqd": 1
},
{
@@ -219,6 +219,13 @@
"fieldtype": "Small Text",
"label": "Error",
"read_only": 1
},
{
"fieldname": "posting_time",
"fieldtype": "Time",
"label": "Posting Time",
"no_copy": 1,
"reqd": 1
}
],
"is_submittable": 1,
@@ -228,10 +235,11 @@
"link_fieldname": "pos_closing_entry"
}
],
"modified": "2021-10-20 16:19:25.340565",
"modified": "2022-08-01 11:37:14.991228",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Closing Entry",
"naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
{
@@ -278,5 +286,6 @@
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -15,6 +15,9 @@ from erpnext.controllers.status_updater import StatusUpdater
class POSClosingEntry(StatusUpdater):
def validate(self):
self.posting_date = self.posting_date or frappe.utils.nowdate()
self.posting_time = self.posting_time or frappe.utils.nowtime()
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"))

View File

@@ -6,6 +6,7 @@
"engine": "InnoDB",
"field_order": [
"posting_date",
"posting_time",
"merge_invoices_based_on",
"column_break_3",
"pos_closing_entry",
@@ -105,12 +106,19 @@
"label": "Customer Group",
"mandatory_depends_on": "eval:doc.merge_invoices_based_on == 'Customer Group'",
"options": "Customer Group"
},
{
"fieldname": "posting_time",
"fieldtype": "Time",
"label": "Posting Time",
"no_copy": 1,
"reqd": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2021-09-14 11:17:19.001142",
"modified": "2022-08-01 11:36:42.456429",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice Merge Log",
@@ -173,5 +181,6 @@
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -9,7 +9,7 @@ from frappe import _
from frappe.core.page.background_jobs.background_jobs import get_info
from frappe.model.document import Document
from frappe.model.mapper import map_child_doc, map_doc
from frappe.utils import cint, flt, getdate, nowdate
from frappe.utils import cint, flt, get_time, getdate, nowdate, nowtime
from frappe.utils.background_jobs import enqueue
from frappe.utils.scheduler import is_scheduler_inactive
@@ -99,6 +99,7 @@ class POSInvoiceMergeLog(Document):
sales_invoice.is_consolidated = 1
sales_invoice.set_posting_time = 1
sales_invoice.posting_date = getdate(self.posting_date)
sales_invoice.posting_time = get_time(self.posting_time)
sales_invoice.save()
sales_invoice.submit()
@@ -115,6 +116,7 @@ class POSInvoiceMergeLog(Document):
credit_note.is_consolidated = 1
credit_note.set_posting_time = 1
credit_note.posting_date = getdate(self.posting_date)
credit_note.posting_time = get_time(self.posting_time)
# TODO: return could be against multiple sales invoice which could also have been consolidated?
# credit_note.return_against = self.consolidated_invoice
credit_note.save()
@@ -402,6 +404,9 @@ def create_merge_logs(invoice_by_customer, closing_entry=None):
merge_log.posting_date = (
getdate(closing_entry.get("posting_date")) if closing_entry else nowdate()
)
merge_log.posting_time = (
get_time(closing_entry.get("posting_time")) if closing_entry else nowtime()
)
merge_log.customer = customer
merge_log.pos_closing_entry = closing_entry.get("name") if closing_entry else None

View File

@@ -1791,4 +1791,6 @@ def make_purchase_receipt(source_name, target_doc=None):
target_doc,
)
doc.set_onload("ignore_price_list", True)
return doc

View File

@@ -479,9 +479,13 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
is_cash_or_non_trade_discount() {
this.frm.set_df_property("additional_discount_account", "hidden", 1 - this.frm.doc.is_cash_or_non_trade_discount);
this.frm.set_df_property("additional_discount_account", "reqd", this.frm.doc.is_cash_or_non_trade_discount);
if (!this.frm.doc.is_cash_or_non_trade_discount) {
this.frm.set_value("additional_discount_account", "");
}
this.calculate_taxes_and_totals();
}
};

View File

@@ -1033,22 +1033,6 @@ class SalesInvoice(SellingController):
)
)
if self.apply_discount_on == "Grand Total" and self.get("is_cash_or_discount_account"):
gl_entries.append(
self.get_gl_dict(
{
"account": self.additional_discount_account,
"against": self.debit_to,
"debit": self.base_discount_amount,
"debit_in_account_currency": self.discount_amount,
"cost_center": self.cost_center,
"project": self.project,
},
self.currency,
item=self,
)
)
def make_tax_gl_entries(self, gl_entries):
enable_discount_accounting = cint(
frappe.db.get_single_value("Selling Settings", "enable_discount_accounting")
@@ -2103,13 +2087,13 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
target_detail_field = "sales_invoice_item" if doctype == "Sales Invoice" else "sales_order_item"
source_document_warehouse_field = "target_warehouse"
target_document_warehouse_field = "from_warehouse"
received_items = get_received_items(source_name, target_doctype, target_detail_field)
else:
source_doc = frappe.get_doc(doctype, source_name)
target_doctype = "Sales Invoice" if doctype == "Purchase Invoice" else "Sales Order"
source_document_warehouse_field = "from_warehouse"
target_document_warehouse_field = "target_warehouse"
received_items = get_received_items(source_name, target_doctype, target_detail_field)
received_items = {}
validate_inter_company_transaction(source_doc, doctype)
details = get_inter_company_details(source_doc, doctype)

View File

@@ -318,7 +318,6 @@ def get_advance_vouchers(
"is_cancelled": 0,
"party_type": party_type,
"party": ["in", parties],
"against_voucher": ["is", "not set"],
}
if company:

View File

@@ -207,7 +207,7 @@ def set_address_details(
)
if company_address:
party_details.update({"company_address": company_address})
party_details.company_address = company_address
else:
party_details.update(get_company_address(company))
@@ -219,12 +219,31 @@ def set_address_details(
get_regional_address_details(party_details, doctype, company)
elif doctype and doctype in ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]:
if party_details.company_address:
party_details["shipping_address"] = shipping_address or party_details["company_address"]
party_details.shipping_address_display = get_address_display(party_details["shipping_address"])
if shipping_address:
party_details.update(
get_fetch_values(doctype, "shipping_address", party_details.shipping_address)
shipping_address=shipping_address,
shipping_address_display=get_address_display(shipping_address),
**get_fetch_values(doctype, "shipping_address", shipping_address)
)
if party_details.company_address:
# billing address
party_details.update(
billing_address=party_details.company_address,
billing_address_display=(
party_details.company_address_display or get_address_display(party_details.company_address)
),
**get_fetch_values(doctype, "billing_address", party_details.company_address)
)
# shipping address - if not already set
if not party_details.shipping_address:
party_details.update(
shipping_address=party_details.billing_address,
shipping_address_display=party_details.billing_address_display,
**get_fetch_values(doctype, "shipping_address", party_details.billing_address)
)
get_regional_address_details(party_details, doctype, company)
return party_details.get(billing_address_field), party_details.shipping_address_name

View File

@@ -165,7 +165,7 @@ class ReceivablePayableReport(object):
"range4",
"range5",
"future_amount",
"remaining_balance"
"remaining_balance",
]
def get_voucher_balance(self, ple):

View File

@@ -44,14 +44,14 @@ frappe.query_reports["Gross Profit"] = {
"parent_field": "parent_invoice",
"initial_depth": 3,
"formatter": function(value, row, column, data, default_formatter) {
if (column.fieldname == "sales_invoice" && column.options == "Item" && data.indent == 0) {
if (column.fieldname == "sales_invoice" && column.options == "Item" && data && data.indent == 0) {
column._options = "Sales Invoice";
} else {
column._options = "Item";
}
value = default_formatter(value, row, column, data);
if (data && (data.indent == 0.0 || row[1].content == "Total")) {
if (data && (data.indent == 0.0 || (row[1] && row[1].content == "Total"))) {
value = $(`<span>${value}</span>`);
var $value = $(value).css("font-weight", "bold");
value = $value.wrap("<p></p>").parent().html();

View File

@@ -616,7 +616,7 @@ class GrossProfitGenerator(object):
previous_stock_value = len(my_sle) > i + 1 and flt(my_sle[i + 1].stock_value) or 0.0
if previous_stock_value:
return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
return abs(previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
else:
return flt(row.qty) * self.get_average_buying_rate(row, item_code)
else:

View File

@@ -14,9 +14,9 @@ def execute(filters=None):
filters.naming_series = frappe.db.get_single_value("Buying Settings", "supp_master_name")
columns = get_columns(filters)
tds_docs, tds_accounts, tax_category_map = get_tds_docs(filters)
tds_docs, tds_accounts, tax_category_map, journal_entry_party_map = get_tds_docs(filters)
res = get_result(filters, tds_docs, tds_accounts, tax_category_map)
res = get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_party_map)
final_result = group_by_supplier_and_category(res)
return columns, final_result

View File

@@ -26,7 +26,6 @@ def get_result(filters, tds_docs, tds_accounts, tax_category_map, journal_entry_
supplier_map = get_supplier_pan_map()
tax_rate_map = get_tax_rate_map(filters)
gle_map = get_gle_map(tds_docs)
print(journal_entry_party_map)
out = []
for name, details in gle_map.items():

View File

@@ -823,7 +823,13 @@ def get_held_invoices(party_type, party):
def get_outstanding_invoices(
party_type, party, account, common_filter=None, min_outstanding=None, max_outstanding=None
party_type,
party,
account,
common_filter=None,
posting_date=None,
min_outstanding=None,
max_outstanding=None,
):
ple = qb.DocType("Payment Ledger Entry")
@@ -850,6 +856,7 @@ def get_outstanding_invoices(
ple_query = QueryPaymentLedger()
invoice_list = ple_query.get_voucher_outstandings(
common_filter=common_filter,
posting_date=posting_date,
min_outstanding=min_outstanding,
max_outstanding=max_outstanding,
get_invoices=True,
@@ -1501,6 +1508,7 @@ class QueryPaymentLedger(object):
# query filters
self.vouchers = []
self.common_filter = []
self.voucher_posting_date = []
self.min_outstanding = None
self.max_outstanding = None
@@ -1571,6 +1579,7 @@ class QueryPaymentLedger(object):
.where(ple.delinked == 0)
.where(Criterion.all(filter_on_voucher_no))
.where(Criterion.all(self.common_filter))
.where(Criterion.all(self.voucher_posting_date))
.groupby(ple.voucher_type, ple.voucher_no, ple.party_type, ple.party)
)
@@ -1652,6 +1661,7 @@ class QueryPaymentLedger(object):
self,
vouchers=None,
common_filter=None,
posting_date=None,
min_outstanding=None,
max_outstanding=None,
get_payments=False,
@@ -1671,6 +1681,7 @@ class QueryPaymentLedger(object):
self.reset()
self.vouchers = vouchers
self.common_filter = common_filter or []
self.voucher_posting_date = posting_date or []
self.min_outstanding = min_outstanding
self.max_outstanding = max_outstanding
self.get_payments = get_payments

View File

@@ -15,9 +15,12 @@ frappe.ui.form.on("Request for Quotation",{
frm.fields_dict["suppliers"].grid.get_field("contact").get_query = function(doc, cdt, cdn) {
let d = locals[cdt][cdn];
return {
query: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_supplier_contacts",
filters: {'supplier': d.supplier}
}
query: "frappe.contacts.doctype.contact.contact.contact_query",
filters: {
link_doctype: "Supplier",
link_name: d.supplier || ""
}
};
}
},

View File

@@ -286,18 +286,6 @@ def get_list_context(context=None):
return list_context
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_supplier_contacts(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql(
"""select `tabContact`.name from `tabContact`, `tabDynamic Link`
where `tabDynamic Link`.link_doctype = 'Supplier' and (`tabDynamic Link`.link_name=%(name)s
and `tabDynamic Link`.link_name like %(txt)s) and `tabContact`.name = `tabDynamic Link`.parent
limit %(page_len)s offset %(start)s""",
{"start": start, "page_len": page_len, "txt": "%%%s%%" % txt, "name": filters.get("supplier")},
)
@frappe.whitelist()
def make_supplier_quotation_from_rfq(source_name, target_doc=None, for_supplier=None):
def postprocess(source, target_doc):

View File

@@ -4,6 +4,8 @@
# Decompiled by https://python-decompiler.com
import copy
import frappe
from frappe.tests.utils import FrappeTestCase
@@ -11,10 +13,12 @@ from erpnext.buying.report.subcontracted_item_to_be_received.subcontracted_item_
execute,
)
from erpnext.controllers.tests.test_subcontracting_controller import (
get_rm_items,
get_subcontracting_order,
make_service_item,
make_stock_in_entry,
make_stock_transfer_entry,
)
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
make_subcontracting_receipt,
)
@@ -36,15 +40,18 @@ class TestSubcontractedItemToBeReceived(FrappeTestCase):
sco = get_subcontracting_order(
service_items=service_items, supplier_warehouse="_Test Warehouse 1 - _TC"
)
make_stock_entry(
item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100
)
make_stock_entry(
item_code="_Test Item Home Desktop 100",
target="_Test Warehouse 1 - _TC",
qty=100,
basic_rate=100,
rm_items = get_rm_items(sco.supplied_items)
itemwise_details = make_stock_in_entry(rm_items=rm_items)
for item in rm_items:
item["sco_rm_detail"] = sco.items[0].name
make_stock_transfer_entry(
sco_no=sco.name,
rm_items=rm_items,
itemwise_details=copy.deepcopy(itemwise_details),
)
make_subcontracting_receipt_against_sco(sco.name)
sco.reload()
col, data = execute(

View File

@@ -0,0 +1,105 @@
# Version 14.0.0 Release Notes
### Accounting
- [Improved Indian Compliance and GST APIs](https://docs.erpnext.com/docs/v14/user/manual/en/regional/india)
- [Common Party Accounting](https://docs.erpnext.com/docs/v14/user/manual/en/accounts/articles/common_party_accounting)
- [Provisional accounting for expenses](https://github.com/frappe/erpnext/pull/29451)
- [Discount Accounting](https://github.com/frappe/erpnext/pull/26359)
- [New Payment Reconciliation Tool](https://docs.erpnext.com/docs/v13/user/manual/en/accounts/payment-reconciliation)
- [Coupon Code in POS](https://github.com/frappe/erpnext/pull/27004)
- [Configurable cost center allocation](https://docs.erpnext.com/docs/v14/user/manual/en/cost_center_allocation)
- [Payment Ledger](https://docs.erpnext.com/docs/v14/user/manual/en/accounts/articles/payment_ledger)
- [Cash and Non trade discounts in Sales Invoice](https://github.com/frappe/erpnext/pull/31405)
- [Improved TaxJar Integration](https://docs.erpnext.com/docs/v14/user/manual/en/erpnext_integration/taxjar_integration)
- [KSA E-Invoicing](https://docs.erpnext.com/docs/v14/user/manual/en/simplified_ksa_vat_management_and_reporting)
- [South Africa VAT Audit Report](https://docs.erpnext.com/docs/v14/user/manual/en/regional/south_africa/vat_audit_report)
- [E Invoice Eway Bill Distance is calculated automatically](https://github.com/frappe/erpnext/pull/30908)
- [Payment Terms Status report](https://github.com/frappe/erpnext/pull/29137)
- [Merge POS invoices based on customer group](https://github.com/frappe/erpnext/pull/27471)
- [Ledger Merger](https://github.com/frappe/erpnext/pull/28812)
- [Increase number of supported currency exchanges](https://github.com/frappe/erpnext/pull/26763)
### Stock
- [LIFO Valuation](https://github.com/frappe/erpnext/pull/29296)
- [Batch-wise Valuation Rates](https://github.com/frappe/erpnext/pull/29804)
- [Better Barcode Scanning](https://github.com/frappe/erpnext/pull/30516)
- [Over transfer allowance for material transfers](https://github.com/frappe/erpnext/pull/26264)
- [Scanning in Pick List](https://github.com/frappe/erpnext/pull/30832)
- [GLE reposting with progress and chunking for backdated entries](https://github.com/frappe/erpnext/pull/31343)
### E-Commerce
- [Redesigned E-commerce Portal](https://docs.erpnext.com/docs/v13/user/manual/en/e_commerce/set_up_e_commerce)
- [E-commerce Search](https://docs.erpnext.com/docs/v14/user/manual/en/e_commerce/e_commerce_search)
### Assets
- [Asset Splitting](https://github.com/frappe/erpnext/pull/29350)
- [Grouped Asset](https://github.com/frappe/erpnext/pull/29334)
- [Asset Repair](https://github.com/frappe/erpnext/pull/25798)
- [Consume serialized items during Asset Repair](https://github.com/frappe/erpnext/pull/28349)
### Manufacturing
- [Faster BOM Update Tool](https://github.com/frappe/erpnext/pull/31078)
- [Scrap Item in Job Card](https://github.com/frappe/erpnext/pull/27518)
- [Process Loss in manufacturing](https://github.com/frappe/erpnext/pull/26151)
- [Production Plan Summary Report](https://github.com/frappe/erpnext/pull/26240)
- [Work Order Consumed Materials Report](https://github.com/frappe/erpnext/pull/28500)
- [Provision to close the Work Order](https://github.com/frappe/erpnext/pull/28150)
- [Provision to aggregate subassembly items in production plan](https://github.com/frappe/erpnext/pull/28939)
### Subcontracting
- [New Subcontracting Module](https://github.com/frappe/erpnext/pull/30955)
- [Subcontracted Purchase Order from the Production Plan](https://github.com/frappe/erpnext/pull/26240)
### CRM
- [Refreshed CRM Flows](https://github.com/frappe/erpnext/pull/31311)
- [New Prospect document](https://github.com/frappe/erpnext/pull/27102)
- [CRM Settings Page](https://docs.erpnext.com/docs/v13/user/manual/en/CRM/crm_settings)
- [Competitor Tagging in Opportunity and Quotation](https://github.com/frappe/erpnext/pull/28050)
- [Sales Pipeline Analytics Report](https://github.com/frappe/erpnext/pull/26639)
- [Opportunity Summary by Sales Stage Report](https://github.com/frappe/erpnext/pull/26639)
### HR & Payroll
- [Organizational Chart](https://github.com/frappe/erpnext/pull/26261)
- [Full and Final Settlement](https://github.com/frappe/erpnext/pull/26364)
- [Income tax computation Report](https://github.com/frappe/erpnext/pull/29963)
- [Employee Grievance](https://github.com/frappe/erpnext/pull/25705)
- [Tax for recurring additional salary](https://github.com/frappe/erpnext/pull/27459)
- [Tracking Multi-round interview](https://github.com/frappe/erpnext/pull/25482)
- [Exit Interview and Employee Exits Report](https://github.com/frappe/erpnext/pull/28741)
- [Leave Type configuration to allow over allocation](https://github.com/frappe/erpnext/pull/30940)
- [Employee Reminders](https://github.com/frappe/erpnext/pull/25735)
- [Refactored Employee Leave Balance](https://github.com/frappe/erpnext/pull/29439)
### Healthcare
- [Treatment Plan Template](https://github.com/frappe/erpnext/pull/26557)
- [Capacity for Service Unit, concurrent appointments based on capacity, Patient Appointments](https://github.com/frappe/erpnext/pull/27219)
- [UOM specific barcode](https://docs.erpnext.com/docs/v14/user/manual/en/stock/articles/track-items-using-barcode#uom-specific-barcode)
- [Redesigned Patient History and Patient Progress](https://github.com/frappe/erpnext/pull/27100)
### New apps
The following modules has been separated out from ERPNext and new apps has been created.
- [HR and Payroll](https://github.com/frappe/hrms)
- [Healthcare](https://github.com/frappe/health)
- [Education](https://github.com/frappe/education)
- [E-commerce Integration](https://github.com/frappe/ecommerce_integrations)
- [Hospitality](https://github.com/frappe/hospitality)
- [Non-Profit](https://github.com/frappe/non_profit)
- [Agriculture](https://github.com/frappe/agriculture)
- [Datev Integration](https://github.com/alyf-de/erpnext_datev)
- [Germany Localisation](https://github.com/alyf-de/erpnext_germany)
### Others
- [Unicommerce Integration](https://docs.erpnext.com/docs/v13/user/manual/en/erpnext_integration/unicommerce_integration)
- [Bulk Transaction Processing](https://github.com/frappe/erpnext/pull/28580)
- [Refactored Document Naming Settings](https://docs.erpnext.com/docs/v14/user/manual/en/setting-up/settings/document-naming-settings)
- [Project Portal Enhancements](https://github.com/frappe/erpnext/pull/26090)
- [Refund entry against loans](https://github.com/frappe/erpnext/pull/29460)
- [Bank Reconciliation for loan documents](https://github.com/frappe/erpnext/pull/29865)

View File

@@ -1109,17 +1109,17 @@ class AccountsController(TransactionBase):
frappe.db.get_single_value("Selling Settings", "enable_discount_accounting")
)
if self.doctype == "Purchase Invoice":
dr_or_cr = "credit"
rev_dr_cr = "debit"
supplier_or_customer = self.supplier
else:
dr_or_cr = "debit"
rev_dr_cr = "credit"
supplier_or_customer = self.customer
if enable_discount_accounting:
if self.doctype == "Purchase Invoice":
dr_or_cr = "credit"
rev_dr_cr = "debit"
supplier_or_customer = self.supplier
else:
dr_or_cr = "debit"
rev_dr_cr = "credit"
supplier_or_customer = self.customer
for item in self.get("items"):
if item.get("discount_amount") and item.get("discount_account"):
discount_amount = item.discount_amount * item.qty
@@ -1173,18 +1173,22 @@ class AccountsController(TransactionBase):
)
)
if self.get("discount_amount") and self.get("additional_discount_account"):
gl_entries.append(
self.get_gl_dict(
{
"account": self.additional_discount_account,
"against": supplier_or_customer,
dr_or_cr: self.discount_amount,
"cost_center": self.cost_center,
},
item=self,
)
if (
(enable_discount_accounting or self.get("is_cash_or_non_trade_discount"))
and self.get("additional_discount_account")
and self.get("discount_amount")
):
gl_entries.append(
self.get_gl_dict(
{
"account": self.additional_discount_account,
"against": supplier_or_customer,
dr_or_cr: self.discount_amount,
"cost_center": self.cost_center,
},
item=self,
)
)
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
from erpnext.controllers.status_updater import get_allowance_for

View File

@@ -86,6 +86,7 @@ class BuyingController(SubcontractingController):
company=self.company,
party_address=self.get("supplier_address"),
shipping_address=self.get("shipping_address"),
company_address=self.get("billing_address"),
fetch_payment_terms_template=not self.get("ignore_default_payment_terms_template"),
ignore_permissions=self.flags.ignore_permissions,
)

View File

@@ -18,8 +18,9 @@ from erpnext.stock.get_item_details import _get_item_tax_template
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def employee_query(doctype, txt, searchfield, start, page_len, filters):
doctype = "Employee"
conditions = []
fields = get_fields("Employee", ["name", "employee_name"])
fields = get_fields(doctype, ["name", "employee_name"])
return frappe.db.sql(
"""select {fields} from `tabEmployee`
@@ -49,7 +50,8 @@ def employee_query(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def lead_query(doctype, txt, searchfield, start, page_len, filters):
fields = get_fields("Lead", ["name", "lead_name", "company_name"])
doctype = "Lead"
fields = get_fields(doctype, ["name", "lead_name", "company_name"])
return frappe.db.sql(
"""select {fields} from `tabLead`
@@ -77,6 +79,7 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def customer_query(doctype, txt, searchfield, start, page_len, filters):
doctype = "Customer"
conditions = []
cust_master_name = frappe.defaults.get_user_default("cust_master_name")
@@ -85,9 +88,9 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters):
else:
fields = ["name", "customer_name", "customer_group", "territory"]
fields = get_fields("Customer", fields)
fields = get_fields(doctype, fields)
searchfields = frappe.get_meta("Customer").get_search_fields()
searchfields = frappe.get_meta(doctype).get_search_fields()
searchfields = " or ".join(field + " like %(txt)s" for field in searchfields)
return frappe.db.sql(
@@ -116,6 +119,7 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def supplier_query(doctype, txt, searchfield, start, page_len, filters):
doctype = "Supplier"
supp_master_name = frappe.defaults.get_user_default("supp_master_name")
if supp_master_name == "Supplier Name":
@@ -123,7 +127,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters):
else:
fields = ["name", "supplier_name", "supplier_group"]
fields = get_fields("Supplier", fields)
fields = get_fields(doctype, fields)
return frappe.db.sql(
"""select {field} from `tabSupplier`
@@ -147,6 +151,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
doctype = "Account"
company_currency = erpnext.get_company_currency(filters.get("company"))
def get_accounts(with_account_type_filter):
@@ -197,13 +202,14 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
doctype = "Item"
conditions = []
if isinstance(filters, str):
filters = json.loads(filters)
# Get searchfields from meta and use in Item Link field query
meta = frappe.get_meta("Item", cached=True)
meta = frappe.get_meta(doctype, cached=True)
searchfields = meta.get_search_fields()
# these are handled separately
@@ -257,7 +263,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
filters.pop("supplier", None)
description_cond = ""
if frappe.db.count("Item", cache=True) < 50000:
if frappe.db.count(doctype, cache=True) < 50000:
# scan description only if items are less than 50000
description_cond = "or tabItem.description LIKE %(txt)s"
return frappe.db.sql(
@@ -300,8 +306,9 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def bom(doctype, txt, searchfield, start, page_len, filters):
doctype = "BOM"
conditions = []
fields = get_fields("BOM", ["name", "item"])
fields = get_fields(doctype, ["name", "item"])
return frappe.db.sql(
"""select {fields}
@@ -331,6 +338,7 @@ def bom(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_project_name(doctype, txt, searchfield, start, page_len, filters):
doctype = "Project"
cond = ""
if filters and filters.get("customer"):
cond = """(`tabProject`.customer = %s or
@@ -338,8 +346,8 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters):
frappe.db.escape(filters.get("customer"))
)
fields = get_fields("Project", ["name", "project_name"])
searchfields = frappe.get_meta("Project").get_search_fields()
fields = get_fields(doctype, ["name", "project_name"])
searchfields = frappe.get_meta(doctype).get_search_fields()
searchfields = " or ".join(["`tabProject`." + field + " like %(txt)s" for field in searchfields])
return frappe.db.sql(
@@ -366,7 +374,8 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict):
fields = get_fields("Delivery Note", ["name", "customer", "posting_date"])
doctype = "Delivery Note"
fields = get_fields(doctype, ["name", "customer", "posting_date"])
return frappe.db.sql(
"""
@@ -402,6 +411,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len,
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
doctype = "Batch"
cond = ""
if filters.get("posting_date"):
cond = "and (batch.expiry_date is null or batch.expiry_date >= %(posting_date)s)"
@@ -420,7 +430,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
if filters.get("is_return"):
having_clause = ""
meta = frappe.get_meta("Batch", cached=True)
meta = frappe.get_meta(doctype, cached=True)
searchfields = meta.get_search_fields()
search_columns = ""
@@ -496,6 +506,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_account_list(doctype, txt, searchfield, start, page_len, filters):
doctype = "Account"
filter_list = []
if isinstance(filters, dict):
@@ -514,7 +525,7 @@ def get_account_list(doctype, txt, searchfield, start, page_len, filters):
filter_list.append([doctype, searchfield, "like", "%%%s%%" % txt])
return frappe.desk.reportview.execute(
"Account",
doctype,
filters=filter_list,
fields=["name", "parent_account"],
limit_start=start,
@@ -553,6 +564,7 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters):
if not filters:
filters = {}
doctype = "Account"
condition = ""
if filters.get("company"):
condition += "and tabAccount.company = %(company)s"
@@ -628,6 +640,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
if not filters:
filters = {}
doctype = "Account"
condition = ""
if filters.get("company"):
condition += "and tabAccount.company = %(company)s"
@@ -650,6 +663,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
@frappe.validate_and_sanitize_search_inputs
def warehouse_query(doctype, txt, searchfield, start, page_len, filters):
# Should be used when item code is passed in filters.
doctype = "Warehouse"
conditions, bin_conditions = [], []
filter_dict = get_doctype_wise_filters(filters)

View File

@@ -36,6 +36,10 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError):
pass
class BatchExpiredError(frappe.ValidationError):
pass
class StockController(AccountsController):
def validate(self):
super(StockController, self).validate()
@@ -77,6 +81,10 @@ class StockController(AccountsController):
def validate_serialized_batch(self):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
is_material_issue = False
if self.doctype == "Stock Entry" and self.purpose == "Material Issue":
is_material_issue = True
for d in self.get("items"):
if hasattr(d, "serial_no") and hasattr(d, "batch_no") and d.serial_no and d.batch_no:
serial_nos = frappe.get_all(
@@ -93,6 +101,9 @@ class StockController(AccountsController):
)
)
if is_material_issue:
continue
if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2:
expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date")
@@ -100,7 +111,8 @@ class StockController(AccountsController):
frappe.throw(
_("Row #{0}: The batch {1} has already expired.").format(
d.idx, get_link_to_form("Batch", d.get("batch_no"))
)
),
BatchExpiredError,
)
def clean_serial_nos(self):
@@ -310,7 +322,13 @@ class StockController(AccountsController):
)
if (
self.doctype
not in ("Purchase Receipt", "Purchase Invoice", "Stock Reconciliation", "Stock Entry")
not in (
"Purchase Receipt",
"Purchase Invoice",
"Stock Reconciliation",
"Stock Entry",
"Subcontracting Receipt",
)
and not is_expense_account
):
frappe.throw(
@@ -374,9 +392,24 @@ class StockController(AccountsController):
def update_inventory_dimensions(self, row, sl_dict) -> None:
dimensions = get_evaluated_inventory_dimension(row, sl_dict, parent_doc=self)
for dimension in dimensions:
if dimension and row.get(dimension.source_fieldname):
if not dimension:
continue
if row.get(dimension.source_fieldname):
sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname)
if not sl_dict.get(dimension.target_fieldname) and dimension.fetch_from_parent:
sl_dict[dimension.target_fieldname] = self.get(dimension.fetch_from_parent)
# Get value based on doctype name
if not sl_dict.get(dimension.target_fieldname):
fieldname = frappe.get_cached_value(
"DocField", {"parent": self.doctype, "options": dimension.fetch_from_parent}, "fieldname"
)
if fieldname and self.get(fieldname):
sl_dict[dimension.target_fieldname] = self.get(fieldname)
def make_sl_entries(self, sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False):
from erpnext.stock.stock_ledger import make_sl_entries

View File

@@ -490,7 +490,7 @@ class SubcontractingController(StockController):
row.item_code,
row.get(self.subcontract_data.order_field),
) and transfer_item.qty > 0:
qty = self.__get_qty_based_on_material_transfer(row, transfer_item) or 0
qty = flt(self.__get_qty_based_on_material_transfer(row, transfer_item))
transfer_item.qty -= qty
self.__add_supplied_item(row, transfer_item.get("item_details"), qty)
@@ -720,6 +720,25 @@ class SubcontractingController(StockController):
sco_doc = frappe.get_doc("Subcontracting Order", sco)
sco_doc.update_status()
def set_missing_values_in_additional_costs(self):
self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs"))
if self.total_additional_costs:
if self.distribute_additional_costs_based_on == "Amount":
total_amt = sum(flt(item.amount) for item in self.get("items"))
for item in self.items:
item.additional_cost_per_qty = (
(item.amount * self.total_additional_costs) / total_amt
) / item.qty
else:
total_qty = sum(flt(item.qty) for item in self.get("items"))
additional_cost_per_qty = self.total_additional_costs / total_qty
for item in self.items:
item.additional_cost_per_qty = additional_cost_per_qty
else:
for item in self.items:
item.additional_cost_per_qty = 0
@frappe.whitelist()
def get_current_stock(self):
if self.doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
@@ -730,7 +749,7 @@ class SubcontractingController(StockController):
{"item_code": item.rm_item_code, "warehouse": self.supplier_warehouse},
"actual_qty",
)
item.current_stock = flt(actual_qty) or 0
item.current_stock = flt(actual_qty)
@property
def sub_contracted_items(self):

View File

@@ -37,6 +37,11 @@ class calculate_taxes_and_totals(object):
self.set_discount_amount()
self.apply_discount_amount()
# Update grand total as per cash and non trade discount
if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
self.doc.grand_total -= self.doc.discount_amount
self.doc.base_grand_total -= self.doc.base_discount_amount
self.calculate_shipping_charges()
if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
@@ -500,9 +505,6 @@ class calculate_taxes_and_totals(object):
else:
self.doc.grand_total = flt(self.doc.net_total)
if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
self.doc.grand_total -= self.doc.discount_amount
if self.doc.get("taxes"):
self.doc.total_taxes_and_charges = flt(
self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
@@ -597,16 +599,16 @@ class calculate_taxes_and_totals(object):
if not self.doc.apply_discount_on:
frappe.throw(_("Please select Apply Discount On"))
self.doc.base_discount_amount = flt(
self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
)
if self.doc.apply_discount_on == "Grand Total" and self.doc.get(
"is_cash_or_non_trade_discount"
):
self.discount_amount_applied = True
return
self.doc.base_discount_amount = flt(
self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
)
total_for_discount_amount = self.get_total_for_discount_amount()
taxes = self.doc.get("taxes")
net_total = 0

View File

@@ -36,6 +36,36 @@ class TestSubcontractingController(FrappeTestCase):
sco.remove_empty_rows()
self.assertEqual((len_before - 1), len(sco.service_items))
def test_set_missing_values_in_additional_costs(self):
sco = get_subcontracting_order(do_not_submit=1)
rate_without_additional_cost = sco.items[0].rate
amount_without_additional_cost = sco.items[0].amount
additional_amount = 120
sco.append(
"additional_costs",
{
"expense_account": "Cost of Goods Sold - _TC",
"description": "Test",
"amount": additional_amount,
},
)
sco.save()
additional_cost_per_qty = additional_amount / sco.items[0].qty
self.assertEqual(sco.items[0].additional_cost_per_qty, additional_cost_per_qty)
self.assertEqual(rate_without_additional_cost + additional_cost_per_qty, sco.items[0].rate)
self.assertEqual(amount_without_additional_cost + additional_amount, sco.items[0].amount)
sco.additional_costs = []
sco.save()
self.assertEqual(sco.items[0].additional_cost_per_qty, 0)
self.assertEqual(rate_without_additional_cost, sco.items[0].rate)
self.assertEqual(amount_without_additional_cost, sco.items[0].amount)
def test_create_raw_materials_supplied(self):
sco = get_subcontracting_order()
sco.supplied_items = None

View File

@@ -340,8 +340,8 @@
"fieldname": "no_of_employees",
"fieldtype": "Select",
"label": "No of Employees",
"options": "1-10\n11-20\n21-30\n31-100\n11-50\n51-200\n201-500\n101-500\n500-1000\n501-1000\n>1000\n1000+"
},
"options": "1-10\n11-50\n51-200\n201-500\n501-1000\n1000+"
},
{
"fieldname": "column_break_22",
"fieldtype": "Column Break"
@@ -514,7 +514,7 @@
"idx": 5,
"image_field": "image",
"links": [],
"modified": "2022-07-22 15:55:03.176094",
"modified": "2022-08-09 18:26:17.101521",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",

View File

@@ -463,7 +463,7 @@
"fieldname": "no_of_employees",
"fieldtype": "Select",
"label": "No of Employees",
"options": "1-10\n11-20\n21-30\n31-100\n11-50\n51-200\n201-500\n101-500\n500-1000\n501-1000\n>1000\n1000+"
"options": "1-10\n11-50\n51-200\n201-500\n501-1000\n1000+"
},
{
"fieldname": "annual_revenue",
@@ -622,7 +622,7 @@
"icon": "fa fa-info-sign",
"idx": 195,
"links": [],
"modified": "2022-07-22 18:46:32.858696",
"modified": "2022-08-09 18:26:37.235964",
"modified_by": "Administrator",
"module": "CRM",
"name": "Opportunity",

View File

@@ -82,7 +82,7 @@
"fieldname": "no_of_employees",
"fieldtype": "Select",
"label": "No. of Employees",
"options": "1-10\n11-20\n21-30\n31-100\n11-50\n51-200\n201-500\n101-500\n500-1000\n501-1000\n>1000\n1000+"
"options": "1-10\n11-50\n51-200\n201-500\n501-1000\n1000+"
},
{
"fieldname": "annual_revenue",
@@ -218,7 +218,7 @@
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2022-06-22 15:10:26.887502",
"modified": "2022-08-09 18:26:56.950185",
"modified_by": "Administrator",
"module": "CRM",
"name": "Prospect",

View File

@@ -12,7 +12,9 @@ from decimal import Decimal
import frappe
from bs4 import BeautifulSoup as bs
from frappe import _
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from frappe.custom.doctype.custom_field.custom_field import (
create_custom_fields as _create_custom_fields,
)
from frappe.model.document import Document
from frappe.utils.data import format_datetime
@@ -577,22 +579,25 @@ class TallyMigration(Document):
new_year.save()
oldest_year = new_year
def create_custom_fields(doctypes):
tally_guid_df = {
"fieldtype": "Data",
"fieldname": "tally_guid",
"read_only": 1,
"label": "Tally GUID",
}
tally_voucher_no_df = {
"fieldtype": "Data",
"fieldname": "tally_voucher_no",
"read_only": 1,
"label": "Tally Voucher Number",
}
for df in [tally_guid_df, tally_voucher_no_df]:
for doctype in doctypes:
create_custom_field(doctype, df)
def create_custom_fields():
_create_custom_fields(
{
("Journal Entry", "Purchase Invoice", "Sales Invoice"): [
{
"fieldtype": "Data",
"fieldname": "tally_guid",
"read_only": 1,
"label": "Tally GUID",
},
{
"fieldtype": "Data",
"fieldname": "tally_voucher_no",
"read_only": 1,
"label": "Tally Voucher Number",
},
]
}
)
def create_price_list():
frappe.get_doc(
@@ -628,7 +633,7 @@ class TallyMigration(Document):
create_fiscal_years(vouchers)
create_price_list()
create_custom_fields(["Journal Entry", "Purchase Invoice", "Sales Invoice"])
create_custom_fields()
total = len(vouchers)
is_last = False

View File

@@ -6,7 +6,7 @@ from urllib.parse import urlparse
import frappe
from frappe import _
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.model.document import Document
from frappe.utils.nestedset import get_root_of
@@ -19,27 +19,24 @@ class WoocommerceSettings(Document):
def create_delete_custom_fields(self):
if self.enable_sync:
custom_fields = {}
# create
for doctype in ["Customer", "Sales Order", "Item", "Address"]:
df = dict(
fieldname="woocommerce_id",
label="Woocommerce ID",
fieldtype="Data",
read_only=1,
print_hide=1,
)
create_custom_field(doctype, df)
for doctype in ["Customer", "Address"]:
df = dict(
fieldname="woocommerce_email",
label="Woocommerce Email",
fieldtype="Data",
read_only=1,
print_hide=1,
)
create_custom_field(doctype, df)
create_custom_fields(
{
("Customer", "Sales Order", "Item", "Address"): dict(
fieldname="woocommerce_id",
label="Woocommerce ID",
fieldtype="Data",
read_only=1,
print_hide=1,
),
("Customer", "Address"): dict(
fieldname="woocommerce_email",
label="Woocommerce Email",
fieldtype="Data",
read_only=1,
print_hide=1,
),
}
)
if not frappe.get_value("Item Group", {"name": _("WooCommerce Products")}):
item_group = frappe.new_doc("Item Group")

View File

@@ -507,6 +507,7 @@ accounting_dimension_doctypes = [
"Shipping Rule",
"Landed Cost Item",
"Asset Value Adjustment",
"Asset Repair",
"Loyalty Program",
"Stock Reconciliation",
"POS Profile",
@@ -519,6 +520,10 @@ accounting_dimension_doctypes = [
"Purchase Order",
"Purchase Receipt",
"Sales Order",
"Subcontracting Order",
"Subcontracting Order Item",
"Subcontracting Receipt",
"Subcontracting Receipt Item",
]
# get matching queries for Bank Reconciliation

View File

@@ -135,7 +135,11 @@ def calculate_accrual_amount_for_demand_loans(
def make_accrual_interest_entry_for_demand_loans(
posting_date, process_loan_interest, open_loans=None, loan_type=None, accrual_type="Regular"
):
query_filters = {"status": ("in", ["Disbursed", "Partially Disbursed"]), "docstatus": 1}
query_filters = {
"status": ("in", ["Disbursed", "Partially Disbursed"]),
"docstatus": 1,
"is_term_loan": 0,
}
if loan_type:
query_filters.update({"loan_type": loan_type})
@@ -229,6 +233,7 @@ def get_term_loans(date, term_loan=None, loan_type=None):
AND l.is_term_loan =1
AND rs.payment_date <= %s
AND rs.is_accrued=0 {0}
AND rs.interest_amount > 0
AND l.status = 'Disbursed'
ORDER BY rs.payment_date""".format(
condition

View File

@@ -189,8 +189,8 @@ class BOM(WebsiteGenerator):
self.validate_transfer_against()
self.set_routing_operations()
self.validate_operations()
self.update_exploded_items(save=False)
self.calculate_cost()
self.update_exploded_items(save=False)
self.update_stock_qty()
self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate=False, save=False)
self.validate_scrap_items()

View File

@@ -611,6 +611,34 @@ class TestBOM(FrappeTestCase):
bom.reload()
self.assertEqual(frappe.get_value("Item", fg_item.item_code, "default_bom"), bom.name)
def test_exploded_items_rate(self):
rm_item = make_item(
properties={"is_stock_item": 1, "valuation_rate": 99, "last_purchase_rate": 89}
).name
fg_item = make_item(properties={"is_stock_item": 1}).name
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
bom = make_bom(item=fg_item, raw_materials=[rm_item], do_not_save=True)
bom.rm_cost_as_per = "Last Purchase Rate"
bom.save()
self.assertEqual(bom.items[0].base_rate, 89)
self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
bom.rm_cost_as_per = "Price List"
bom.save()
self.assertEqual(bom.items[0].base_rate, 0.0)
self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
bom.rm_cost_as_per = "Valuation Rate"
bom.save()
self.assertEqual(bom.items[0].base_rate, 99)
self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
bom.submit()
self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
def get_default_bom(item_code="_Test FG Item 2"):
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})

View File

@@ -184,6 +184,7 @@
"in_list_view": 1,
"label": "Rate",
"options": "currency",
"read_only": 1,
"reqd": 1
},
{
@@ -288,7 +289,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2022-05-19 02:32:43.785470",
"modified": "2022-07-28 10:20:51.559010",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Item",

View File

@@ -482,7 +482,6 @@ class ProductionPlan(Document):
"bom_no",
"stock_uom",
"bom_level",
"production_plan_item",
"schedule_date",
]:
if row.get(field):
@@ -639,6 +638,9 @@ class ProductionPlan(Document):
sub_assembly_items_store = [] # temporary store to process all subassembly items
for row in self.po_items:
if not row.item_code:
frappe.throw(_("Row #{0}: Please select Item Code in Assembly Items").format(row.idx))
bom_data = []
get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty)
self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)

View File

@@ -11,8 +11,9 @@ from erpnext.manufacturing.doctype.production_plan.production_plan import (
get_warehouse_list,
)
from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError
from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry as make_se_from_wo
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.doctype.item.test_item import create_item
from erpnext.stock.doctype.item.test_item import create_item, make_item
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
create_stock_reconciliation,
@@ -583,9 +584,6 @@ class TestProductionPlan(FrappeTestCase):
Test Prod Plan impact via: SO -> Prod Plan -> WO -> SE -> SE (cancel)
"""
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
from erpnext.manufacturing.doctype.work_order.work_order import (
make_stock_entry as make_se_from_wo,
)
make_stock_entry(
item_code="Raw Material Item 1", target="Work In Progress - _TC", qty=2, basic_rate=100
@@ -629,9 +627,6 @@ class TestProductionPlan(FrappeTestCase):
def test_production_plan_pending_qty_independent_items(self):
"Test Prod Plan impact if items are added independently (no from SO or MR)."
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
from erpnext.manufacturing.doctype.work_order.work_order import (
make_stock_entry as make_se_from_wo,
)
make_stock_entry(
item_code="Raw Material Item 1", target="Work In Progress - _TC", qty=2, basic_rate=100
@@ -728,6 +723,57 @@ class TestProductionPlan(FrappeTestCase):
for po_item, subassy_item in zip(pp.po_items, pp.sub_assembly_items):
self.assertEqual(po_item.name, subassy_item.production_plan_item)
def test_produced_qty_for_multi_level_bom_item(self):
# Create Items and BOMs
rm_item = make_item(properties={"is_stock_item": 1}).name
sub_assembly_item = make_item(properties={"is_stock_item": 1}).name
fg_item = make_item(properties={"is_stock_item": 1}).name
make_stock_entry(
item_code=rm_item,
qty=60,
to_warehouse="Work In Progress - _TC",
rate=99,
purpose="Material Receipt",
)
make_bom(item=sub_assembly_item, raw_materials=[rm_item], rm_qty=3)
make_bom(item=fg_item, raw_materials=[sub_assembly_item], rm_qty=4)
# Step - 1: Create Production Plan
pln = create_production_plan(item_code=fg_item, planned_qty=5, skip_getting_mr_items=1)
pln.get_sub_assembly_items()
# Step - 2: Create Work Orders
pln.make_work_order()
work_orders = frappe.get_all("Work Order", filters={"production_plan": pln.name}, pluck="name")
sa_wo = fg_wo = None
for work_order in work_orders:
wo_doc = frappe.get_doc("Work Order", work_order)
if wo_doc.production_plan_item:
wo_doc.update(
{"wip_warehouse": "Work In Progress - _TC", "fg_warehouse": "Finished Goods - _TC"}
)
fg_wo = wo_doc.name
else:
wo_doc.update(
{"wip_warehouse": "Work In Progress - _TC", "fg_warehouse": "Work In Progress - _TC"}
)
sa_wo = wo_doc.name
wo_doc.submit()
# Step - 3: Complete Work Orders
se = frappe.get_doc(make_se_from_wo(sa_wo, "Manufacture"))
se.submit()
se = frappe.get_doc(make_se_from_wo(fg_wo, "Manufacture"))
se.submit()
# Step - 4: Check Production Plan Item Produced Qty
pln.load_from_db()
self.assertEqual(pln.status, "Completed")
self.assertEqual(pln.po_items[0].produced_qty, 5)
def create_production_plan(**args):
"""

View File

@@ -268,6 +268,7 @@ erpnext.patches.v13_0.enable_ksa_vat_docs #1
erpnext.patches.v13_0.show_india_localisation_deprecation_warning
erpnext.patches.v13_0.show_hr_payroll_deprecation_warning
erpnext.patches.v13_0.reset_corrupt_defaults
erpnext.patches.v13_0.create_accounting_dimensions_for_asset_repair
[post_model_sync]
execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings')
@@ -308,4 +309,6 @@ erpnext.patches.v14_0.migrate_gl_to_payment_ledger
erpnext.patches.v14_0.crm_ux_cleanup
erpnext.patches.v14_0.remove_india_localisation # 14-07-2022
erpnext.patches.v13_0.fix_number_and_frequency_for_monthly_depreciation
erpnext.patches.v14_0.remove_hr_and_payroll_modules # 20-07-2022
erpnext.patches.v14_0.remove_hr_and_payroll_modules # 20-07-2022
erpnext.patches.v14_0.fix_crm_no_of_employees
erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes

View File

@@ -0,0 +1,29 @@
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
def execute():
accounting_dimensions = frappe.db.get_all(
"Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"]
)
if not accounting_dimensions:
return
for d in accounting_dimensions:
doctype = "Asset Repair"
field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname})
if field:
continue
df = {
"fieldname": d.fieldname,
"label": d.label,
"fieldtype": "Link",
"options": d.document_type,
"insert_after": "accounting_dimensions_section",
}
create_custom_field(doctype, df, ignore_validate=True)
frappe.clear_cache(doctype=doctype)

View File

@@ -0,0 +1,47 @@
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
def execute():
accounting_dimensions = frappe.db.get_all(
"Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"]
)
if not accounting_dimensions:
return
count = 1
for d in accounting_dimensions:
if count % 2 == 0:
insert_after_field = "dimension_col_break"
else:
insert_after_field = "accounting_dimensions_section"
for doctype in [
"Subcontracting Order",
"Subcontracting Order Item",
"Subcontracting Receipt",
"Subcontracting Receipt Item",
]:
field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname})
if field:
continue
df = {
"fieldname": d.fieldname,
"label": d.label,
"fieldtype": "Link",
"options": d.document_type,
"insert_after": insert_after_field,
}
try:
create_custom_field(doctype, df, ignore_validate=True)
frappe.clear_cache(doctype=doctype)
except Exception:
pass
count += 1

View File

@@ -0,0 +1,26 @@
import frappe
def execute():
options = {
"11-20": "11-50",
"21-30": "11-50",
"31-100": "51-200",
"101-500": "201-500",
"500-1000": "501-1000",
">1000": "1000+",
}
for doctype in ("Lead", "Opportunity", "Prospect"):
frappe.reload_doctype(doctype)
for key, value in options.items():
frappe.db.sql(
"""
update `tab{doctype}`
set no_of_employees = %s
where no_of_employees = %s
""".format(
doctype=doctype
),
(value, key),
)

View File

@@ -57,7 +57,11 @@ class TestHomepageSection(unittest.TestCase):
self.assertEqual(cards[0].h5.text, "Card 1")
self.assertEqual(cards[0].a["href"], "/card-1")
self.assertEqual(cards[1].p.text, "Subtitle 2")
self.assertEqual(cards[1].find(class_="website-image-lazy")["data-src"], "test.jpg")
img = cards[1].find(class_="card-img-top")
self.assertEqual(img["src"], "test.jpg")
self.assertEqual(img["loading"], "lazy")
# cleanup
frappe.db.rollback()

View File

@@ -379,7 +379,7 @@ def get_users_for_project(doctype, txt, searchfield, start, page_len, filters):
{fcond} {mcond}
order by
(case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
(case when locate(%(_txt)s, full_name) > 0 then locate(%(_txt)s, full_name) else 99999 end)
(case when locate(%(_txt)s, full_name) > 0 then locate(%(_txt)s, full_name) else 99999 end),
idx desc,
name, full_name
limit %(page_len)s offset %(start)s""".format(

View File

@@ -39,6 +39,12 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
this._calculate_taxes_and_totals();
this.calculate_discount_amount();
// # Update grand total as per cash and non trade discount
if (this.frm.doc.apply_discount_on == "Grand Total" && this.frm.doc.is_cash_or_non_trade_discount) {
this.frm.doc.grand_total -= this.frm.doc.discount_amount;
this.frm.doc.base_grand_total -= this.frm.doc.base_discount_amount;
}
await this.calculate_shipping_charges();
// Advance calculation applicable to Sales /Purchase Invoice
@@ -633,6 +639,10 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
this.frm.doc.base_discount_amount = flt(this.frm.doc.discount_amount * this.frm.doc.conversion_rate,
precision("base_discount_amount"));
if (this.frm.doc.apply_discount_on == "Grand Total" && this.frm.doc.is_cash_or_non_trade_discount) {
return;
}
var total_for_discount_amount = this.get_total_for_discount_amount();
var net_total = 0;
// calculate item amount after Discount Amount

View File

@@ -1,7 +1,6 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
frappe.provide('erpnext.accounts.dimensions');
erpnext.TransactionController = class TransactionController extends erpnext.taxes_and_totals {
setup() {
@@ -794,24 +793,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
set_party_account(set_pricing);
});
// Get default company billing address in Purchase Invoice, Order and Receipt
if (this.frm.doc.company && frappe.meta.get_docfield(this.frm.doctype, "billing_address")) {
frappe.call({
method: "erpnext.setup.doctype.company.company.get_default_company_address",
args: {name: this.frm.doc.company, existing_address: this.frm.doc.billing_address || ""},
debounce: 2000,
callback: function(r) {
if (r.message) {
me.frm.set_value("billing_address", r.message);
} else {
if (frappe.meta.get_docfield(me.frm.doctype, 'company_address')) {
me.frm.set_value("company_address", "");
}
}
}
});
}
} else {
set_party_account(set_pricing);
}

View File

@@ -3,25 +3,14 @@
frappe.provide("erpnext.utils");
const SALES_DOCTYPES = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice'];
const PURCHASE_DOCTYPES = ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'];
erpnext.utils.get_party_details = function(frm, method, args, callback) {
if (!method) {
method = "erpnext.accounts.party.get_party_details";
}
if (args) {
if (in_list(['Sales Invoice', 'Sales Order', 'Delivery Note'], frm.doc.doctype)) {
if (frm.doc.company_address && (!args.company_address)) {
args.company_address = frm.doc.company_address;
}
}
if (in_list(['Purchase Invoice', 'Purchase Order', 'Purchase Receipt'], frm.doc.doctype)) {
if (frm.doc.shipping_address && (!args.shipping_address)) {
args.shipping_address = frm.doc.shipping_address;
}
}
}
if (!args) {
if ((frm.doctype != "Purchase Order" && frm.doc.customer)
|| (frm.doc.party_name && in_list(['Quotation', 'Opportunity'], frm.doc.doctype))) {
@@ -45,41 +34,44 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
};
}
if (in_list(['Sales Invoice', 'Sales Order', 'Delivery Note'], frm.doc.doctype)) {
if (!args) {
if (!args) {
if (in_list(SALES_DOCTYPES, frm.doc.doctype)) {
args = {
party: frm.doc.customer || frm.doc.party_name,
party_type: 'Customer'
}
}
if (frm.doc.company_address && (!args.company_address)) {
args.company_address = frm.doc.company_address;
};
}
if (frm.doc.shipping_address_name &&(!args.shipping_address_name)) {
args.shipping_address_name = frm.doc.shipping_address_name;
}
}
if (in_list(['Purchase Invoice', 'Purchase Order', 'Purchase Receipt'], frm.doc.doctype)) {
if (!args) {
if (in_list(PURCHASE_DOCTYPES, frm.doc.doctype)) {
args = {
party: frm.doc.supplier,
party_type: 'Supplier'
}
}
if (frm.doc.shipping_address && (!args.shipping_address)) {
args.shipping_address = frm.doc.shipping_address;
};
}
}
if (args) {
args.posting_date = frm.doc.posting_date || frm.doc.transaction_date;
args.fetch_payment_terms_template = cint(!frm.doc.ignore_default_payment_terms_template);
if (!args || !args.party) return;
args.posting_date = frm.doc.posting_date || frm.doc.transaction_date;
args.fetch_payment_terms_template = cint(!frm.doc.ignore_default_payment_terms_template);
}
if (in_list(SALES_DOCTYPES, frm.doc.doctype)) {
if (!args.company_address && frm.doc.company_address) {
args.company_address = frm.doc.company_address;
}
}
if (!args || !args.party) return;
if (in_list(PURCHASE_DOCTYPES, frm.doc.doctype)) {
if (!args.company_address && frm.doc.billing_address) {
args.company_address = frm.doc.billing_address;
}
if (!args.shipping_address && frm.doc.shipping_address) {
args.shipping_address = frm.doc.shipping_address;
}
}
if (frappe.meta.get_docfield(frm.doc.doctype, "taxes")) {
if (!erpnext.utils.validate_mandatory(frm, "Posting / Transaction Date",

View File

@@ -142,10 +142,6 @@ def get_item_for_list_in_html(context):
if (context.get("website_image") or "").startswith("files/"):
context["website_image"] = "/" + quote(context["website_image"])
context["show_availability_status"] = cint(
frappe.db.get_single_value("E Commerce Settings", "show_availability_status")
)
products_template = "templates/includes/products_as_list.html"
return frappe.get_template(products_template).render(context)

View File

@@ -4,7 +4,7 @@
import frappe
from frappe import _
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
from frappe.utils import cint
@@ -83,35 +83,32 @@ def setup_currency_exchange():
def create_print_setting_custom_fields():
create_custom_field(
"Print Settings",
create_custom_fields(
{
"label": _("Compact Item Print"),
"fieldname": "compact_item_print",
"fieldtype": "Check",
"default": 1,
"insert_after": "with_letterhead",
},
)
create_custom_field(
"Print Settings",
{
"label": _("Print UOM after Quantity"),
"fieldname": "print_uom_after_quantity",
"fieldtype": "Check",
"default": 0,
"insert_after": "compact_item_print",
},
)
create_custom_field(
"Print Settings",
{
"label": _("Print taxes with zero amount"),
"fieldname": "print_taxes_with_zero_amount",
"fieldtype": "Check",
"default": 0,
"insert_after": "allow_print_for_cancelled",
},
"Print Settings": [
{
"label": _("Compact Item Print"),
"fieldname": "compact_item_print",
"fieldtype": "Check",
"default": "1",
"insert_after": "with_letterhead",
},
{
"label": _("Print UOM after Quantity"),
"fieldname": "print_uom_after_quantity",
"fieldtype": "Check",
"default": "0",
"insert_after": "compact_item_print",
},
{
"label": _("Print taxes with zero amount"),
"fieldname": "print_taxes_with_zero_amount",
"fieldtype": "Check",
"default": "0",
"insert_after": "allow_print_for_cancelled",
},
]
}
)

View File

@@ -473,7 +473,13 @@ def make_new_batch(**args):
"doctype": "Batch",
"batch_id": args.batch_id,
"item": args.item_code,
"expiry_date": args.expiry_date,
}
).insert()
)
if args.expiry_date:
batch.expiry_date = args.expiry_date
batch.insert()
return batch

View File

@@ -35,14 +35,39 @@ frappe.ui.form.on('Inventory Dimension', {
refresh(frm) {
if (frm.doc.__onload && frm.doc.__onload.has_stock_ledger
&& frm.doc.__onload.has_stock_ledger.length) {
let msg = __('Stock transactions exists against this dimension, user can not update document.');
frm.dashboard.add_comment(msg, 'blue', true);
let allow_to_edit_fields = ['disabled', 'fetch_from_parent',
'type_of_transaction', 'condition'];
frm.fields.forEach((field) => {
if (field.df.fieldname !== 'disabled') {
if (!in_list(allow_to_edit_fields, field.df.fieldname)) {
frm.set_df_property(field.df.fieldname, "read_only", "1");
}
});
}
if (!frm.is_new()) {
frm.add_custom_button(__('Delete Dimension'), () => {
frm.trigger('delete_dimension');
});
}
},
delete_dimension(frm) {
let msg = (`
Custom fields related to this dimension will be deleted on deletion of dimension.
<br> Do you want to delete {0} dimension?
`);
frappe.confirm(__(msg, [frm.doc.name.bold()]), () => {
frappe.call({
method: 'erpnext.stock.doctype.inventory_dimension.inventory_dimension.delete_dimension',
args: {
dimension: frm.doc.name
},
callback: function() {
frappe.set_route('List', 'Inventory Dimension');
}
});
});
}
});

View File

@@ -1,6 +1,5 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "field:dimension_name",
"creation": "2022-06-17 13:04:16.554051",
"doctype": "DocType",
@@ -22,6 +21,7 @@
"document_type",
"istable",
"type_of_transaction",
"fetch_from_parent",
"column_break_16",
"condition",
"applicable_condition_example_section",
@@ -101,12 +101,14 @@
"fieldname": "target_fieldname",
"fieldtype": "Data",
"label": "Target Fieldname (Stock Ledger Entry)",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "source_fieldname",
"fieldtype": "Data",
"label": "Source Fieldname",
"no_copy": 1,
"read_only": 1
},
{
@@ -123,7 +125,7 @@
"fieldname": "type_of_transaction",
"fieldtype": "Select",
"label": "Type of Transaction",
"options": "\nInward\nOutward"
"options": "\nInward\nOutward\nBoth"
},
{
"fieldname": "html_19",
@@ -140,11 +142,18 @@
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
},
{
"depends_on": "istable",
"description": "Set fieldname or DocType name like Supplier, Customer etc.",
"fieldname": "fetch_from_parent",
"fieldtype": "Data",
"label": "Fetch Value From Parent Form"
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2022-07-19 21:06:11.824976",
"modified": "2022-08-17 11:43:24.722441",
"modified_by": "Administrator",
"module": "Stock",
"name": "Inventory Dimension",

View File

@@ -43,13 +43,37 @@ class InventoryDimension(Document):
return
old_doc = self._doc_before_save
allow_to_edit_fields = [
"disabled",
"fetch_from_parent",
"type_of_transaction",
"condition",
]
for field in frappe.get_meta("Inventory Dimension").fields:
if field.fieldname != "disabled" and old_doc.get(field.fieldname) != self.get(field.fieldname):
if field.fieldname not in allow_to_edit_fields and old_doc.get(field.fieldname) != self.get(
field.fieldname
):
msg = f"""The user can not change value of the field {bold(field.label)} because
stock transactions exists against the dimension {bold(self.name)}."""
frappe.throw(_(msg), DoNotChangeError)
def on_trash(self):
self.delete_custom_fields()
def delete_custom_fields(self):
filters = {"fieldname": self.source_fieldname}
if self.document_type:
filters["dt"] = self.document_type
for field in frappe.get_all("Custom Field", filters=filters):
frappe.delete_doc("Custom Field", field.name)
msg = f"Deleted custom fields related to the dimension {self.name}"
frappe.msgprint(_(msg))
def reset_value(self):
if self.apply_to_all_doctypes:
self.istable = 0
@@ -76,30 +100,35 @@ class InventoryDimension(Document):
self.add_custom_fields()
def add_custom_fields(self):
dimension_field = dict(
fieldname=self.source_fieldname,
fieldtype="Link",
insert_after="warehouse",
options=self.reference_document,
label=self.dimension_name,
)
dimension_fields = [
dict(
fieldname="inventory_dimension",
fieldtype="Section Break",
insert_after="warehouse",
label="Inventory Dimension",
collapsible=1,
),
dict(
fieldname=self.source_fieldname,
fieldtype="Link",
insert_after="inventory_dimension",
options=self.reference_document,
label=self.dimension_name,
),
]
custom_fields = {}
if self.apply_to_all_doctypes:
for doctype in get_inventory_documents():
if not frappe.db.get_value(
"Custom Field", {"dt": doctype[0], "fieldname": self.source_fieldname}
):
custom_fields.setdefault(doctype[0], dimension_field)
elif not frappe.db.get_value(
"Custom Field", {"dt": self.document_type, "fieldname": self.source_fieldname}
):
custom_fields.setdefault(self.document_type, dimension_field)
custom_fields.setdefault(doctype[0], dimension_fields)
else:
custom_fields.setdefault(self.document_type, dimension_fields)
if not frappe.db.get_value(
"Custom Field", {"dt": "Stock Ledger Entry", "fieldname": self.target_fieldname}
):
dimension_field = dimension_fields[1]
dimension_field["fieldname"] = self.target_fieldname
custom_fields["Stock Ledger Entry"] = dimension_field
@@ -143,7 +172,7 @@ def get_evaluated_inventory_dimension(doc, sl_dict, parent_doc=None):
elif (
row.type_of_transaction == "Outward"
if doc.docstatus == 1
else row.type_of_transaction != "Inward"
else row.type_of_transaction != "Outward"
) and sl_dict.actual_qty > 0:
continue
@@ -166,7 +195,14 @@ def get_document_wise_inventory_dimensions(doctype) -> dict:
if not frappe.local.document_wise_inventory_dimensions.get(doctype):
dimensions = frappe.get_all(
"Inventory Dimension",
fields=["name", "source_fieldname", "condition", "target_fieldname", "type_of_transaction"],
fields=[
"name",
"source_fieldname",
"condition",
"target_fieldname",
"type_of_transaction",
"fetch_from_parent",
],
filters={"disabled": 0},
or_filters={"document_type": doctype, "apply_to_all_doctypes": 1},
)
@@ -194,3 +230,9 @@ def get_inventory_dimensions():
frappe.local.inventory_dimensions = dimensions
return frappe.local.inventory_dimensions
@frappe.whitelist()
def delete_dimension(dimension):
doc = frappe.get_doc("Inventory Dimension", dimension)
doc.delete()

View File

@@ -8,6 +8,7 @@ from erpnext.stock.doctype.inventory_dimension.inventory_dimension import (
CanNotBeChildDoc,
CanNotBeDefaultDimension,
DoNotChangeError,
delete_dimension,
)
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
@@ -42,6 +43,32 @@ class TestInventoryDimension(FrappeTestCase):
self.assertRaises(CanNotBeDefaultDimension, inv_dim1.insert)
def test_delete_inventory_dimension(self):
inv_dim1 = create_inventory_dimension(
reference_document="Shelf",
type_of_transaction="Outward",
dimension_name="From Shelf",
apply_to_all_doctypes=0,
document_type="Stock Entry Detail",
condition="parent.purpose == 'Material Issue'",
)
inv_dim1.save()
custom_field = frappe.db.get_value(
"Custom Field", {"fieldname": "from_shelf", "dt": "Stock Entry Detail"}, "name"
)
self.assertTrue(custom_field)
delete_dimension(inv_dim1.name)
custom_field = frappe.db.get_value(
"Custom Field", {"fieldname": "from_shelf", "dt": "Stock Entry Detail"}, "name"
)
self.assertFalse(custom_field)
def test_inventory_dimension(self):
warehouse = "Shelf Warehouse - _TC"
item_code = "_Test Item"

View File

@@ -792,10 +792,8 @@
{
"fieldname": "expense_account",
"fieldtype": "Link",
"hidden": 1,
"label": "Expense Account",
"options": "Account",
"read_only": 1
"options": "Account"
},
{
"fieldname": "accounting_dimensions_section",
@@ -1001,7 +999,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2022-06-17 05:32:16.483178",
"modified": "2022-07-28 19:27:54.880781",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Item",

View File

@@ -15,7 +15,7 @@ frappe.ui.form.on('Repost Item Valuation', {
return {
filters: {
name: ['in', ['Purchase Receipt', 'Purchase Invoice', 'Delivery Note',
'Sales Invoice', 'Stock Entry', 'Stock Reconciliation']]
'Sales Invoice', 'Stock Entry', 'Stock Reconciliation', 'Subcontracting Receipt']]
}
};
});

View File

@@ -583,18 +583,23 @@ frappe.ui.form.on('Stock Entry', {
},
add_to_transit: function(frm) {
if(frm.doc.add_to_transit && frm.doc.purpose=='Material Transfer') {
frm.set_value('to_warehouse', '');
if(frm.doc.purpose=='Material Transfer') {
var filters = {
'is_group': 0,
'company': frm.doc.company
}
if(frm.doc.add_to_transit){
filters['warehouse_type'] = 'Transit';
frm.set_value('to_warehouse', '');
frm.trigger('set_transit_warehouse');
}
frm.fields_dict.to_warehouse.get_query = function() {
return {
filters:{
'warehouse_type' : 'Transit',
'is_group': 0,
'company': frm.doc.company
}
filters:filters
};
};
frm.trigger('set_transit_warehouse');
}
},

View File

@@ -5,7 +5,7 @@
import frappe
from frappe.permissions import add_user_permission, remove_user_permission
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, flt, nowdate, nowtime
from frappe.utils import add_days, flt, nowdate, nowtime, today
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.stock.doctype.item.test_item import (
@@ -1589,6 +1589,31 @@ class TestStockEntry(FrappeTestCase):
self.assertEqual(obj.items[index].basic_rate, 200)
self.assertEqual(obj.items[index].basic_amount, 2000)
def test_batch_expiry(self):
from erpnext.controllers.stock_controller import BatchExpiredError
from erpnext.stock.doctype.batch.test_batch import make_new_batch
item_code = "Test Batch Expiry Test Item - 001"
item_doc = create_item(item_code=item_code, is_stock_item=1, valuation_rate=10)
item_doc.has_batch_no = 1
item_doc.save()
batch = make_new_batch(
batch_id=frappe.generate_hash("", 5), item_code=item_doc.name, expiry_date=add_days(today(), -1)
)
se = make_stock_entry(
item_code=item_code,
purpose="Material Receipt",
qty=4,
to_warehouse="_Test Warehouse - _TC",
batch_no=batch.name,
do_not_save=True,
)
self.assertRaises(BatchExpiredError, se.save)
def make_serialized_item(**args):
args = frappe._dict(args)

View File

@@ -1,4 +1,4 @@
let document_list = ['Landed Cost Voucher', 'Stock Entry'];
let document_list = ['Landed Cost Voucher', 'Stock Entry', 'Subcontracting Order', 'Subcontracting Receipt'];
document_list.forEach((doctype) => {
frappe.ui.form.on(doctype, {

View File

@@ -3,6 +3,8 @@
frappe.provide('erpnext.buying');
{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
frappe.ui.form.on('Subcontracting Order', {
setup: (frm) => {
frm.get_field("items").grid.cannot_add_rows = true;
@@ -136,6 +138,16 @@ frappe.ui.form.on('Subcontracting Order', {
}
});
frappe.ui.form.on('Landed Cost Taxes and Charges', {
amount: function (frm, cdt, cdn) {
frm.events.set_base_amount(frm, cdt, cdn);
},
expense_account: function (frm, cdt, cdn) {
frm.events.set_account_currency(frm, cdt, cdn);
}
});
erpnext.buying.SubcontractingOrderController = class SubcontractingOrderController {
setup() {
this.frm.custom_make_buttons = {

View File

@@ -19,6 +19,10 @@
"transaction_date",
"schedule_date",
"amended_from",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project",
"address_and_contact_section",
"supplier_address",
"address_display",
@@ -422,12 +426,34 @@
"fieldtype": "Select",
"label": "Distribute Additional Costs Based On ",
"options": "Qty\nAmount"
},
{
"collapsible": 1,
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
"label": "Accounting Dimensions"
},
{
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
},
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
}
],
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
"modified": "2022-04-11 21:02:44.097841",
"modified": "2022-08-15 14:08:49.204218",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Order",

View File

@@ -82,25 +82,6 @@ class SubcontractingOrder(SubcontractingController):
self.set_missing_values_in_supplied_items()
self.set_missing_values_in_items()
def set_missing_values_in_additional_costs(self):
if self.get("additional_costs"):
self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs"))
if self.total_additional_costs:
if self.distribute_additional_costs_based_on == "Amount":
total_amt = sum(flt(item.amount) for item in self.get("items"))
for item in self.items:
item.additional_cost_per_qty = (
(item.amount * self.total_additional_costs) / total_amt
) / item.qty
else:
total_qty = sum(flt(item.qty) for item in self.get("items"))
additional_cost_per_qty = self.total_additional_costs / total_qty
for item in self.items:
item.additional_cost_per_qty = additional_cost_per_qty
else:
self.total_additional_costs = 0
def set_missing_values_in_service_items(self):
for idx, item in enumerate(self.get("service_items")):
self.items[idx].service_cost_per_qty = item.amount / self.items[idx].qty
@@ -114,9 +95,7 @@ class SubcontractingOrder(SubcontractingController):
def set_missing_values_in_items(self):
total_qty = total = 0
for item in self.items:
item.rate = (
item.rm_cost_per_qty + item.service_cost_per_qty + (item.additional_cost_per_qty or 0)
)
item.rate = item.rm_cost_per_qty + item.service_cost_per_qty + flt(item.additional_cost_per_qty)
item.amount = item.qty * item.rate
total_qty += flt(item.qty)
total += flt(item.amount)
@@ -187,7 +166,7 @@ class SubcontractingOrder(SubcontractingController):
total_required_qty = total_supplied_qty = 0
for item in self.supplied_items:
total_required_qty += item.required_qty
total_supplied_qty += item.supplied_qty or 0
total_supplied_qty += flt(item.supplied_qty)
if total_supplied_qty:
status = "Partial Material Transferred"
if total_supplied_qty >= total_required_qty:

View File

@@ -40,6 +40,10 @@
"manufacture_section",
"manufacturer",
"manufacturer_part_no",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project",
"section_break_34",
"page_break"
],
@@ -304,13 +308,35 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
{
"collapsible": 1,
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
"label": "Accounting Dimensions"
},
{
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
},
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2022-04-11 21:28:06.585338",
"modified": "2022-08-15 14:25:45.177703",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Order Item",

View File

@@ -3,6 +3,8 @@
frappe.provide('erpnext.buying');
{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
frappe.ui.form.on('Subcontracting Receipt', {
setup: (frm) => {
frm.get_field('supplied_items').grid.cannot_add_rows = true;
@@ -48,6 +50,13 @@ frappe.ui.form.on('Subcontracting Receipt', {
is_group: 0
}
}));
frm.set_query("expense_account", "items", function () {
return {
query: "erpnext.controllers.queries.get_expense_account",
filters: { 'company': frm.doc.company }
};
});
},
refresh: (frm) => {
@@ -121,6 +130,16 @@ frappe.ui.form.on('Subcontracting Receipt', {
},
});
frappe.ui.form.on('Landed Cost Taxes and Charges', {
amount: function (frm, cdt, cdn) {
frm.events.set_base_amount(frm, cdt, cdn);
},
expense_account: function (frm, cdt, cdn) {
frm.events.set_account_currency(frm, cdt, cdn);
}
});
frappe.ui.form.on('Subcontracting Receipt Item', {
item_code(frm) {
set_missing_values(frm);

View File

@@ -15,8 +15,13 @@
"company",
"posting_date",
"posting_time",
"set_posting_time",
"is_return",
"return_against",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project",
"section_addresses",
"supplier_address",
"contact_person",
@@ -43,12 +48,14 @@
"raw_material_details",
"get_current_stock",
"supplied_items",
"additional_costs_section",
"distribute_additional_costs_based_on",
"additional_costs",
"total_additional_costs",
"section_break_46",
"in_words",
"bill_no",
"bill_date",
"accounting_details_section",
"provisional_expense_account",
"more_info",
"status",
"column_break_39",
@@ -132,6 +139,7 @@
"label": "Date",
"no_copy": 1,
"print_width": "100px",
"read_only_depends_on": "eval: !doc.set_posting_time",
"reqd": 1,
"search_index": 1,
"width": "100px"
@@ -144,6 +152,7 @@
"no_copy": 1,
"print_hide": 1,
"print_width": "100px",
"read_only_depends_on": "eval: !doc.set_posting_time",
"reqd": 1,
"width": "100px"
},
@@ -520,19 +529,6 @@
"options": "Company",
"read_only": 1
},
{
"collapsible": 1,
"fieldname": "accounting_details_section",
"fieldtype": "Section Break",
"label": "Accounting Details"
},
{
"fieldname": "provisional_expense_account",
"fieldtype": "Link",
"hidden": 1,
"label": "Provisional Expense Account",
"options": "Account"
},
{
"default": "0",
"fieldname": "is_return",
@@ -569,11 +565,70 @@
{
"fieldname": "section_break_47",
"fieldtype": "Section Break"
},
{
"collapsible": 1,
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
"label": "Accounting Dimensions "
},
{
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
},
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"collapsible": 1,
"collapsible_depends_on": "total_additional_costs",
"depends_on": "eval:(doc.docstatus == 0 || doc.total_additional_costs)",
"fieldname": "additional_costs_section",
"fieldtype": "Section Break",
"label": "Additional Costs"
},
{
"default": "Qty",
"fieldname": "distribute_additional_costs_based_on",
"fieldtype": "Select",
"label": "Distribute Additional Costs Based On ",
"options": "Qty\nAmount"
},
{
"fieldname": "additional_costs",
"fieldtype": "Table",
"label": "Additional Costs",
"options": "Landed Cost Taxes and Charges"
},
{
"fieldname": "total_additional_costs",
"fieldtype": "Currency",
"label": "Total Additional Costs",
"print_hide_if_no_value": 1,
"read_only": 1
},
{
"default": "0",
"depends_on": "eval:doc.docstatus==0",
"fieldname": "set_posting_time",
"fieldtype": "Check",
"label": "Edit Posting Date and Time",
"print_hide": 1
}
],
"in_create": 1,
"is_submittable": 1,
"links": [],
"modified": "2022-04-18 13:15:12.011682",
"modified": "2022-08-22 17:30:40.827517",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Receipt",

View File

@@ -3,7 +3,7 @@
import frappe
from frappe import _
from frappe.utils import cint, getdate, nowdate
from frappe.utils import cint, flt, getdate, nowdate
from erpnext.controllers.subcontracting_controller import SubcontractingController
@@ -103,6 +103,7 @@ class SubcontractingReceipt(SubcontractingController):
@frappe.whitelist()
def set_missing_values(self):
self.set_missing_values_in_additional_costs()
self.set_missing_values_in_supplied_items()
self.set_missing_values_in_items()
@@ -125,12 +126,12 @@ class SubcontractingReceipt(SubcontractingController):
item.rm_cost_per_qty = item.rm_supp_cost / item.qty
rm_supp_cost.pop(item.name)
if self.is_new() and item.rm_supp_cost > 0:
if item.recalculate_rate:
item.rate = (
item.rm_cost_per_qty + (item.service_cost_per_qty or 0) + item.additional_cost_per_qty
flt(item.rm_cost_per_qty) + flt(item.service_cost_per_qty) + flt(item.additional_cost_per_qty)
)
item.received_qty = item.qty + (item.rejected_qty or 0)
item.received_qty = item.qty + flt(item.rejected_qty)
item.amount = item.qty * item.rate
total_qty += item.qty
total_amount += item.amount

View File

@@ -29,6 +29,7 @@
"rate_and_amount",
"rate",
"amount",
"recalculate_rate",
"column_break_19",
"rm_cost_per_qty",
"service_cost_per_qty",
@@ -49,15 +50,16 @@
"col_break5",
"batch_no",
"rejected_serial_no",
"expense_account",
"manufacture_details",
"manufacturer",
"column_break_16",
"manufacturer_part_no",
"accounting_details_section",
"expense_account",
"accounting_dimensions_section",
"project",
"dimension_col_break",
"cost_center",
"dimension_col_break",
"project",
"section_break_80",
"page_break"
],
@@ -192,6 +194,8 @@
"label": "Rate",
"options": "currency",
"print_width": "100px",
"read_only": 1,
"read_only_depends_on": "eval: doc.recalculate_rate",
"width": "100px"
},
{
@@ -363,10 +367,8 @@
{
"fieldname": "expense_account",
"fieldtype": "Link",
"hidden": 1,
"label": "Expense Account",
"options": "Account",
"read_only": 1
"options": "Account"
},
{
"collapsible": 1,
@@ -456,12 +458,23 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "accounting_details_section",
"fieldtype": "Section Break",
"label": "Accounting Details"
},
{
"default": "1",
"fieldname": "recalculate_rate",
"fieldtype": "Check",
"label": "Recalculate Rate"
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2022-04-21 12:07:55.899701",
"modified": "2022-08-20 17:16:48.269164",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Receipt Item",

View File

@@ -46,7 +46,7 @@
<div class="col-md-{{ section.column_value }} mb-4">
<div class="card h-100 justify-content-between">
{% if card.image %}
<div class="website-image-lazy" data-class="card-img-top h-75" data-src="{{ card.image }}" data-alt="{{ card.title }}"></div>
<img class="card-img-top h-75" src="{{ card.image }}" loading="lazy" alt="{{ card.title }}"></img>
{% endif %}
<div class="card-body">
<h5 class="card-title">{{ card.title }}</h5>

View File

@@ -37,7 +37,7 @@
{% for item in homepage.products %}
<div class="col-md-4 mb-4">
<div class="card h-100 justify-content-between">
<div class="website-image-lazy" data-class="card-img-top website-image-extra-large" data-src="{{ item.image }}" data-alt="{{ item.item_name }}"></div>
<img class="card-img-top website-image-extra-large" src="{{ item.image }}" loading="lazy" alt="{{ item.item_name }}"></img>
<div class="card-body flex-grow-0">
<h5 class="card-title">{{ item.item_name }}</h5>
<a href="{{ item.route }}" class="card-link">{{ _('More details') }}</a>