Compare commits

...

218 Commits

Author SHA1 Message Date
Frappe PR Bot
138133e11d chore(release): Bumped to Version 14.27.5
## [14.27.5](https://github.com/frappe/erpnext/compare/v14.27.4...v14.27.5) (2023-06-15)

### Bug Fixes

* typeerror on exchange rate revaluation ([d934dda](d934dda410))
2023-06-15 08:10:29 +00:00
ruthra kumar
582ed2c7f1 Merge pull request #35706 from frappe/mergify/bp/version-14/pr-35701
fix: typeerror on exchange rate revaluation (backport #35701)
2023-06-15 13:38:59 +05:30
ruthra kumar
d934dda410 fix: typeerror on exchange rate revaluation
(cherry picked from commit f8273f7db6)
2023-06-15 06:55:03 +00:00
Frappe PR Bot
c774c14b13 chore(release): Bumped to Version 14.27.4
## [14.27.4](https://github.com/frappe/erpnext/compare/v14.27.3...v14.27.4) (2023-06-15)

### Bug Fixes

* update `Stock Reconciliation` diff qty while reposting (backport [#35700](https://github.com/frappe/erpnext/issues/35700)) (backport [#35702](https://github.com/frappe/erpnext/issues/35702)) ([#35703](https://github.com/frappe/erpnext/issues/35703)) ([f3b7eed](f3b7eedb25))
2023-06-15 06:34:31 +00:00
mergify[bot]
f3b7eedb25 fix: update Stock Reconciliation diff qty while reposting (backport #35700) (backport #35702) (#35703)
fix: update `Stock Reconciliation` diff qty while reposting

(cherry picked from commit 6a1b0a2fab)
(cherry picked from commit bdb5cc8ad4)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-06-15 11:46:28 +05:30
Frappe PR Bot
975010c987 chore(release): Bumped to Version 14.27.3
## [14.27.3](https://github.com/frappe/erpnext/compare/v14.27.2...v14.27.3) (2023-06-14)

### Bug Fixes

* reference error while using exchange rate revaluation ([aa7c81a](aa7c81a0bc))
2023-06-14 15:21:49 +00:00
ruthra kumar
1f6352532e Merge pull request #35696 from frappe/mergify/bp/version-14/pr-35694
fix: reference error while using exchange rate revaluation (backport #35694)
2023-06-14 20:50:16 +05:30
ruthra kumar
aa7c81a0bc fix: reference error while using exchange rate revaluation
(cherry picked from commit cd538e138a)
2023-06-14 15:16:16 +00:00
Frappe PR Bot
08729b49e9 chore(release): Bumped to Version 14.27.2
## [14.27.2](https://github.com/frappe/erpnext/compare/v14.27.1...v14.27.2) (2023-06-14)

### Bug Fixes

* `enqueue_after_commit` wherever it makes sense (backport [#35588](https://github.com/frappe/erpnext/issues/35588)) ([#35590](https://github.com/frappe/erpnext/issues/35590)) ([e505516](e5055160fb))
* `TypeError` in Closing Stock Balance ([32e5bbb](32e5bbbb46))
* **accounts:** validate payment entry references with latest data. ([#31166](https://github.com/frappe/erpnext/issues/31166)) ([4add1b4](4add1b4374))
* added process loss in job card ([6a21d61](6a21d617ce))
* allow user to set rounding loss allowance for accounts balance ([cf14858](cf14858909))
* attribute error on payment reconciliation tool ([25b3c77](25b3c7736b))
* based on status_update.py update opportunity status to converted… ([#35145](https://github.com/frappe/erpnext/issues/35145)) ([dee8275](dee82754ab))
* calculate wdv depr schedule properly for existing assets [v14] ([#35613](https://github.com/frappe/erpnext/issues/35613)) ([feb5d00](feb5d0089b))
* conflicts ([2060a00](2060a003c8))
* CSS not applied to product title ([#35630](https://github.com/frappe/erpnext/issues/35630)) ([2cf871c](2cf871c21e))
* don't set default payment amount in case of invoice return ([#35645](https://github.com/frappe/erpnext/issues/35645)) ([79483cc](79483cc90e))
* Lower deduction certificate not getting applied ([#35667](https://github.com/frappe/erpnext/issues/35667)) ([6f59fa9](6f59fa9e5b))
* Make difference entry button not working ([#35622](https://github.com/frappe/erpnext/issues/35622)) ([043815e](043815e745))
* make showing taxes as table in print configurable (backport [#35672](https://github.com/frappe/erpnext/issues/35672)) ([#35678](https://github.com/frappe/erpnext/issues/35678)) ([f39ae9d](f39ae9dbb1))
* Payment against credit notes will be considered as payment against parent invoice in Accounts Receivable/Payable report ([#35642](https://github.com/frappe/erpnext/issues/35642)) ([81ef2ba](81ef2babe9))
* Project in item-wise sales register ([#35596](https://github.com/frappe/erpnext/issues/35596)) ([7737b90](7737b9061f))
* Stock Reconciliation document update while reposting ([8b617fb](8b617fb75e))
* test case ([7af0380](7af03800c9))
* Update de.csv ([#35278](https://github.com/frappe/erpnext/issues/35278)) ([2077f6e](2077f6e89c))
* Validation for delivery date in Sales Order ([#35597](https://github.com/frappe/erpnext/issues/35597)) ([4a8ce22](4a8ce226f6))
2023-06-14 05:06:16 +00:00
Deepesh Garg
a02469f09c Merge pull request #35664 from frappe/version-14-hotfix
chore: release v14
2023-06-14 10:34:24 +05:30
mergify[bot]
4a8ce226f6 fix: Validation for delivery date in Sales Order (#35597)
fix: Validation for delivery date in Sales Order (#35597)

* fix: Validation for delivery date in Sales Order

* chore: update utils

* chore: revert

* chore: Add default delivery date

(cherry picked from commit 984f89d274)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-06-14 09:01:38 +05:30
mergify[bot]
6f59fa9e5b fix: Lower deduction certificate not getting applied (#35667)
* fix: Lower deduction certificate not getting applied (#35667)

(cherry picked from commit 937c0feefe)

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

* chore: resolve conflicts

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-06-14 09:01:04 +05:30
mergify[bot]
f39ae9dbb1 fix: make showing taxes as table in print configurable (backport #35672) (#35678)
* fix: make showing taxes as table in print configurable (#35672)

(cherry picked from commit 491a50a027)

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

* chore: fix conflict

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-06-13 20:44:05 +05:30
mergify[bot]
4add1b4374 fix(accounts): validate payment entry references with latest data. (#31166)
fix(accounts): validate payment entry references with latest data. (#31166)

* test: payment entry over allocation.

* fix: validate allocated_amount against latest outstanding amount.

* fix: payment entry get outstanding documents for advance payments

* fix: only fetch latest outstanding_amount.

* fix: throw if reference is allocated

* test: throw error if a reference has been partially allocated after inital creation.

* chore: test name

* fix: remove unused part of test

* chore: linter

* chore: more user friendly error messages

* fix: only validate outstanding amount if partly paid and don't filter by cost center

* chore: minor refactor for doc.cost_center

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
(cherry picked from commit 20de27d480)

Co-authored-by: Devin Slauenwhite <devin.slauenwhite@gmail.com>
2023-06-13 19:47:20 +05:30
mergify[bot]
dffa682b80 Stock aging report fix when called in dashboard chart (backport #35671) (#35675)
fix: get_range_age conditions fixed (#35671)

see https://github.com/frappe/erpnext/issues/35669

(cherry picked from commit 9f669d4c2f)

Co-authored-by: Hossein Yousefian <86075967+ihosseinu@users.noreply.github.com>
2023-06-13 19:22:25 +05:30
rohitwaghchaure
74ffb1d59c Merge pull request #35658 from frappe/mergify/bp/version-14-hotfix/pr-35629
fix: added process loss in job card (backport #35629)
2023-06-13 11:52:52 +05:30
rohitwaghchaure
2060a003c8 fix: conflicts 2023-06-13 11:22:59 +05:30
ruthra kumar
9d18b40fb0 Merge pull request #35661 from frappe/mergify/bp/version-14-hotfix/pr-35659
fix: attribute error on payment reconciliation tool (backport #35659)
2023-06-13 10:44:51 +05:30
ruthra kumar
25b3c7736b fix: attribute error on payment reconciliation tool
(cherry picked from commit bada5796fa)
2023-06-13 04:47:20 +00:00
ruthra kumar
6a08d04706 Merge pull request #35660 from frappe/mergify/bp/version-14-hotfix/pr-35620
fix: allow higher debit-credit diff tolerance in Exchange Rate Revaluation (backport #35620)
2023-06-13 10:16:47 +05:30
ruthra kumar
cf14858909 fix: allow user to set rounding loss allowance for accounts balance
(cherry picked from commit 96a0132501)
2023-06-13 04:16:55 +00:00
Rohit Waghchaure
7af03800c9 fix: test case
(cherry picked from commit 0382eecff4)
2023-06-12 17:59:56 +00:00
Rohit Waghchaure
6a21d617ce fix: added process loss in job card
(cherry picked from commit e9a6191af9)

# Conflicts:
#	erpnext/manufacturing/doctype/job_card/job_card.json
2023-06-12 17:59:56 +00:00
Sagar Sharma
48c2c82dc4 Merge pull request #35649 from frappe/mergify/bp/version-14-hotfix/pr-35646
fix: Stock Reconciliation document update while reposting (backport #35646)
2023-06-12 18:45:12 +05:30
s-aga-r
8b617fb75e fix: Stock Reconciliation document update while reposting
(cherry picked from commit db159dd11f)
2023-06-12 13:13:17 +00:00
Anand Baburajan
79483cc90e fix: don't set default payment amount in case of invoice return (#35645) 2023-06-12 18:34:13 +05:30
mergify[bot]
81ef2babe9 fix: Payment against credit notes will be considered as payment against parent invoice in Accounts Receivable/Payable report (#35642)
fix: Payment against credit notes will be considered as payment against parent invoice in Accounts Receivable/Payable report (#35642)

* fix: payment against credit note should be linked to parent invoice

* test: AR/AP report for payment against cr note scenario

* fix: cr_note shows up as outstanding invoice

Payment made against cr_note causes it be reported as outstanding invoice

(cherry picked from commit 42f4f80e0c)

Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2023-06-12 18:06:26 +05:30
mergify[bot]
043815e745 fix: Make difference entry button not working (#35622)
fix: Make difference entry button not working (#35622)

(cherry picked from commit 2f24546b21)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-06-12 15:35:48 +05:30
Trusted Computer
2cf871c21e fix: CSS not applied to product title (#35630) 2023-06-11 19:35:24 +05:30
Frappe PR Bot
0ccb5a34bd chore(release): Bumped to Version 14.27.1
## [14.27.1](https://github.com/frappe/erpnext/compare/v14.27.0...v14.27.1) (2023-06-09)

### Bug Fixes

* calculate wdv depr schedule properly for existing assets [v14] ([#35613](https://github.com/frappe/erpnext/issues/35613)) ([a630db1](a630db1b21))
2023-06-09 16:55:54 +00:00
Anand Baburajan
8372f038c0 Merge pull request #35628 from frappe/mergify/bp/version-14/pr-35613
fix: calculate wdv depr schedule properly for existing assets [v14] (backport #35613)
2023-06-09 22:24:24 +05:30
Anand Baburajan
a630db1b21 fix: calculate wdv depr schedule properly for existing assets [v14] (#35613)
* fix: calculate wdv depr schedule properly for existing assets

* fix: calculate wdv depr schedule properly for existing assets properly

(cherry picked from commit feb5d0089b)
2023-06-09 16:28:14 +00:00
Sagar Sharma
a59dffe42a Merge pull request #35619 from frappe/mergify/bp/version-14-hotfix/pr-35617
fix: `TypeError` in Closing Stock Balance (backport #35617)
2023-06-09 12:24:27 +05:30
s-aga-r
32e5bbbb46 fix: TypeError in Closing Stock Balance
(cherry picked from commit 446253ff39)
2023-06-09 06:07:07 +00:00
Anand Baburajan
feb5d0089b fix: calculate wdv depr schedule properly for existing assets [v14] (#35613)
* fix: calculate wdv depr schedule properly for existing assets

* fix: calculate wdv depr schedule properly for existing assets properly
2023-06-08 23:16:28 +05:30
mergify[bot]
059141df52 refactor: get default contact or address (#35248)
refactor: get default contact or address (#35248)

* refactor: get_party_shipping_address

* refactor: get_default_contact

* chore: adding docstrings

* fix: keep original order

* fix: use get_all instead of get_list

---------

Co-authored-by: ruthra kumar <ruthra@erpnext.com>
(cherry picked from commit b91bb17779)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-06-08 22:04:13 +05:30
mergify[bot]
29e079d5d6 refactor: use delete_contact_and_address (#34497)
refactor: use delete_contact_and_address (#34497)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
(cherry picked from commit 0dde4d4c69)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-06-08 19:59:22 +05:30
mergify[bot]
7737b9061f fix: Project in item-wise sales register (#35596)
fix: Project in item-wise sales register (#35596)

(cherry picked from commit f732cac678)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-06-07 22:36:31 +05:30
mergify[bot]
2077f6e89c fix: Update de.csv (#35278)
fix: Update de.csv (#35278)

added many fixes on the base of 'No' which is often wrongly translated to 'Kein'.

Also removed translation of Naming Seris like ACC-INV-.YYYY.- to ACC-INV-.YYYY.-

(cherry picked from commit c236979508)

Co-authored-by: Wolfram Schmidt <wolfram.schmidt@phamos.eu>
2023-06-07 22:04:14 +05:30
mergify[bot]
dee82754ab fix: based on status_update.py update opportunity status to converted… (#35145)
fix: based on status_update.py update opportunity status to converted… (#35145)

fix: based on status_update.py update opportunity status to converted on sales submit
(cherry picked from commit a9a47a51e4)

Co-authored-by: HarryPaulo <paulo_fabris@hotmail.com>
2023-06-07 22:02:03 +05:30
mergify[bot]
e5055160fb fix: enqueue_after_commit wherever it makes sense (backport #35588) (#35590)
fix: `enqueue_after_commit` wherever it makes sense (#35588)

(cherry picked from commit 4507cb3cd7)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-06-07 15:27:45 +05:30
Ankush Menat
a8ac2a088d chore: extend default role profiles
(cherry picked from commit 0166f69b31)
2023-06-07 15:10:20 +05:30
Frappe PR Bot
0dd16fc97d chore(release): Bumped to Version 14.27.0
# [14.27.0](https://github.com/frappe/erpnext/compare/v14.26.0...v14.27.0) (2023-06-07)

### Bug Fixes

* add validation for existing Serial No Manufactured/Received again ([#35534](https://github.com/frappe/erpnext/issues/35534)) ([4ed701b](4ed701b477))
* get party details ([c7391c8](c7391c8a0f))
* higher precision makes ERR to misjudge zero bal acc as non-zero ([456c336](456c336899))
* ignore `Non-Stock Item` mapping in Pick List ([200166b](200166bc24))
* ignore `Non-Stock Item` while calculating `% Picked` in Sales Order ([c0d8271](c0d82710b7))
* Interest Accrual on Loan Topup ([#35555](https://github.com/frappe/erpnext/issues/35555)) ([6140f13](6140f13f3a))
* missing bom details in the stock entry ([2ee0229](2ee0229751))
* **regional:** allow regional override for updating gl_dict ([#35550](https://github.com/frappe/erpnext/issues/35550)) ([57b502b](57b502b9de))
* Task gantt popup style ([20e2242](20e224224b))
* update `Stock Reconciliation` document while reposting ([cc95ced](cc95cedfee))
* **ux:** throw if no row selected to create repost entries (backport [#35503](https://github.com/frappe/erpnext/issues/35503)) ([#35504](https://github.com/frappe/erpnext/issues/35504)) ([5ba5fb1](5ba5fb1b1c))

### Features

* ability to create quotation against a prospect ([d82d159](d82d1597ac))
2023-06-07 06:18:29 +00:00
Deepesh Garg
35d488d909 Merge pull request #35571 from frappe/version-14-hotfix
chore: release v14
2023-06-07 11:46:26 +05:30
Deepesh Garg
0e82932de1 Merge branch 'version-14' into version-14-hotfix 2023-06-07 10:45:44 +05:30
mergify[bot]
7798bab0d8 chore: Default role profiles (#35584)
chore: Default role profiles (#35584)

(cherry picked from commit 76197cc437)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-06-07 10:30:28 +05:30
mergify[bot]
6140f13f3a fix: Interest Accrual on Loan Topup (#35555)
fix: Interest Accrual on Loan Topup (#35555)

* fix: Interest Accrual on Loan Topup

* chore: CI

* chore: Ignore test

(cherry picked from commit 2ffcca6f10)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-06-07 10:12:12 +05:30
Nabin Hait
c7c1a6e501 Merge pull request #35579 from frappe/mergify/bp/version-14-hotfix/pr-35572
feat: ability to create quotation against a prospect (backport #35572)
2023-06-06 21:55:48 +05:30
Nabin Hait
c7391c8a0f fix: get party details
(cherry picked from commit 5a0aacc0b6)
2023-06-06 16:25:11 +00:00
Nabin Hait
d82d1597ac feat: ability to create quotation against a prospect
(cherry picked from commit 47ce6de57d)
2023-06-06 16:25:11 +00:00
Sagar Vora
307fadc8e2 Merge pull request #35560 from frappe/mergify/bp/version-14-hotfix/pr-35550
fix(regional): allow regional override for updating gl_dict (backport #35550)
2023-06-05 13:58:03 +05:30
Smit Vora
57b502b9de fix(regional): allow regional override for updating gl_dict (#35550)
(cherry picked from commit b1ef19a0cd)
2023-06-05 08:27:44 +00:00
Sagar Sharma
5b92334e95 Merge pull request #35544 from frappe/mergify/bp/version-14-hotfix/pr-35510
fix: ignore `Non-Stock Item` while calculating `% Picked` in Sales Order (backport #35510)
2023-06-03 11:52:19 +05:30
s-aga-r
c0d82710b7 fix: ignore Non-Stock Item while calculating % Picked in Sales Order
(cherry picked from commit 0305a925fe)
2023-06-03 05:48:53 +00:00
s-aga-r
200166bc24 fix: ignore Non-Stock Item mapping in Pick List
(cherry picked from commit 03d7742737)
2023-06-03 05:48:52 +00:00
Sagar Sharma
4ed701b477 fix: add validation for existing Serial No Manufactured/Received again (#35534)
* feat: add new field `Allow existing Serial No` in `Stock Settings`

* fix: add validation for existing Serial No to be Manufactured/Received again
2023-06-03 11:15:55 +05:30
Suraj Shetty
9e71f3263d Merge pull request #35531 from frappe/mergify/bp/version-14-hotfix/pr-35530
fix: Task gantt popup style and info (backport #35530)
2023-06-02 11:10:46 +05:30
Suraj Shetty
20e224224b fix: Task gantt popup style
(cherry picked from commit f7b2d103e7)
2023-06-02 05:39:53 +00:00
rohitwaghchaure
90c79cb24f Merge pull request #35481 from s-aga-r/FIX-ISS-23-24-01138
fix: update `Stock Reconciliation` document while reposting
2023-06-01 21:23:14 +05:30
ruthra kumar
114cb98b8e Merge pull request #35362 from frappe/mergify/bp/version-14-hotfix/pr-35299
refactor: replace join with subquery in PCV (backport #35299)
2023-06-01 15:43:37 +05:30
ruthra kumar
4a62252e7e Merge pull request #35521 from frappe/mergify/bp/version-14-hotfix/pr-35518
fix:higher precision causes ERR to misjudge zero bal acc as non-zero (backport #35518)
2023-06-01 15:39:50 +05:30
ruthra kumar
a6a7cc24a4 Merge pull request #35519 from frappe/mergify/bp/version-14-hotfix/pr-35112
refactor(Gross Profit): simplify group by invoice logic (backport #35112)
2023-06-01 15:12:48 +05:30
ruthra kumar
456c336899 fix: higher precision makes ERR to misjudge zero bal acc as non-zero
(cherry picked from commit 0cd47f07a6)
2023-06-01 09:29:46 +00:00
mergify[bot]
caf886d4a6 chore: typo in pricing rule schema (#35457)
chore: typo in pricing rule schema (#35457)
2023-06-01 14:41:43 +05:30
ruthra kumar
e29ce12a58 refactor: simplify group by invoice logic
(cherry picked from commit 092c4b4c58)
2023-06-01 09:05:35 +00:00
mergify[bot]
1e09a020a9 refactor: Workspace cleanup (backport #35409) (#35512)
refactor: Workspace cleanup (backport #35409) (#35505)

* refactor: Workspace cleanup

(cherry picked from commit 243c49c550)

# Conflicts:
#	erpnext/accounts/workspace/accounting/accounting.json
#	erpnext/assets/workspace/assets/assets.json

* fix: Delete Retail and Utilities worspaces amd hide default Settings and Integration workspaces

(cherry picked from commit 0b28f641ad)

* fix: Added pos links in Selling workspace

(cherry picked from commit 86f88817a9)

* fix: removed duplicate links of manufacturing workspace

(cherry picked from commit 5cf4c8c8b7)

* fix: Rearranged accounting module links

(cherry picked from commit e78a7de1e5)

# Conflicts:
#	erpnext/accounts/workspace/accounting/accounting.json

* chore: typo

(cherry picked from commit bb67cc03df)

* chore: conflicts

---------

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
Co-authored-by: Ankush Menat <ankushmenat@gmail.com>
Co-authored-by: Ankush Menat <ankush@frappe.io>
(cherry picked from commit 841d2e4b2c)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-06-01 11:57:07 +05:30
rohitwaghchaure
6835efa132 Merge pull request #35506 from frappe/mergify/bp/version-14-hotfix/pr-35482
fix: missing bom details in the stock entry (backport #35482)
2023-05-31 18:02:46 +05:30
Rohit Waghchaure
2ee0229751 fix: missing bom details in the stock entry
(cherry picked from commit 2fc7d82324)
2023-05-31 12:00:05 +00:00
s-aga-r
5c9506c8ca test: add test case for update stock reconciliation doc 2023-05-31 16:59:53 +05:30
s-aga-r
cc95cedfee fix: update Stock Reconciliation document while reposting 2023-05-31 16:59:53 +05:30
mergify[bot]
841d2e4b2c refactor: Workspace cleanup (backport #35409) (#35505)
* refactor: Workspace cleanup

(cherry picked from commit 243c49c550)

# Conflicts:
#	erpnext/accounts/workspace/accounting/accounting.json
#	erpnext/assets/workspace/assets/assets.json

* fix: Delete Retail and Utilities worspaces amd hide default Settings and Integration workspaces

(cherry picked from commit 0b28f641ad)

* fix: Added pos links in Selling workspace

(cherry picked from commit 86f88817a9)

* fix: removed duplicate links of manufacturing workspace

(cherry picked from commit 5cf4c8c8b7)

* fix: Rearranged accounting module links

(cherry picked from commit e78a7de1e5)

# Conflicts:
#	erpnext/accounts/workspace/accounting/accounting.json

* chore: typo

(cherry picked from commit bb67cc03df)

* chore: conflicts

---------

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
Co-authored-by: Ankush Menat <ankushmenat@gmail.com>
Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-05-31 16:51:54 +05:30
mergify[bot]
5ba5fb1b1c fix(ux): throw if no row selected to create repost entries (backport #35503) (#35504)
fix(ux): throw if no row selected to create repost entries

(cherry picked from commit 1905239ec2)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-05-31 16:37:52 +05:30
Sagar Vora
9fc798efc0 Merge pull request #35501 from frappe/mergify/bp/version-14-hotfix/pr-35500 2023-05-31 14:46:06 +05:30
Sagar Vora
b10dfba931 chore: remove whitelisting for method not accessed from UI
(cherry picked from commit 517d8a03ec)
2023-05-31 09:15:46 +00:00
Frappe PR Bot
69b214b85b chore(release): Bumped to Version 14.26.0
# [14.26.0](https://github.com/frappe/erpnext/compare/v14.25.1...v14.26.0) (2023-05-31)

### Bug Fixes

* account group totals calculation to consider include_in_gross ([8dcb930](8dcb9302b4))
* add total col for gross and net profit ([cb9b4fb](cb9b4fbb91))
* available qty not fetching for raw material in PP ([746a734](746a734257))
* balance quantity ([56ba7d6](56ba7d6a8a))
* Billing Address display in buying transactions ([#35451](https://github.com/frappe/erpnext/issues/35451)) ([33e8d05](33e8d05718))
* don't map items twice ([6f0c7cf](6f0c7cf9a4))
* Error while validating budget ([#35487](https://github.com/frappe/erpnext/issues/35487)) ([5fd00e7](5fd00e7113))
* **Gross Profit:** 'company' column is ambiguous in filter ([a59c205](a59c205d2e))
* incorrect `POS Reserved Qty` in `Stock Projected Qty` Report ([71e4f34](71e4f34b86))
* incorrect available quantity in BIN ([5a9452f](5a9452f4a3))
* incorrect transferred qty in the job card ([#35478](https://github.com/frappe/erpnext/issues/35478)) ([86801c2](86801c29cb))
* make DN item reference mandatory for Packing Slip Item ([b4e481a](b4e481a390))
* map `Packed Items` while creating `Packing Slip` ([984e32c](984e32c34a))
* monthly WDV depr schedule for existing assets [v14] ([#35458](https://github.com/frappe/erpnext/issues/35458)) ([37d437a](37d437a33c))
* Negative value in Reserved Qty for Production Plan ([6fe42c9](6fe42c937c))
* Packing Slip Item Qty ([5345ebe](5345ebe242))
* **patch:** add patch to set `packed_qty` in draft DN ([b3da2f7](b3da2f7c26))
* rate not fetching properly for inter transfer purchase order ([7b75f45](7b75f454d3))
* remove duplicate items validation ([c7628c9](c7628c98c5))
* retention stock entry: grab conversion factor from source ([bd75584](bd75584c27))
* Show future payments in accounts receivable summary ([#35416](https://github.com/frappe/erpnext/issues/35416)) ([11440cc](11440cca4c))
* Stock Analytics and Warehouse wise Item Balance Age and Value issue ([2058993](205899348a))
* stock onboarding (backport [#35453](https://github.com/frappe/erpnext/issues/35453)) ([#35480](https://github.com/frappe/erpnext/issues/35480)) ([d231b19](d231b19b9f))
* tab-uniformity (backport [#35400](https://github.com/frappe/erpnext/issues/35400)) ([#35402](https://github.com/frappe/erpnext/issues/35402)) ([989052c](989052c075))
* travis ([fe1e2fe](fe1e2fec7a))
* update `Packed Qty` in DN on submit and cancel of `Packing Slip` ([0bed062](0bed06284e))
* **ux:** don't show `Create > Packing Slip` button if items are already packed ([9854c84](9854c84ad8))
* **ux:** get items on selecting DN in Packing Slip ([b96aa75](b96aa75ded))
* **ux:** remove `Get Items` button from `Packing Slip` ([4017342](4017342c15))
* validate Packing Slip Item Qty with DN Items ([cc7e267](cc7e267c35))

### Features

* add field `Packed Qty` in `Delivery Note Item` and `Packed Item` ([509b684](509b68404c))
* add field `pi_detail` in `Packing Slip` ([2b75474](2b75474649))
2023-05-31 06:16:00 +00:00
Deepesh Garg
10afde75da Merge pull request #35473 from frappe/version-14-hotfix
chore: release v14
2023-05-31 11:44:01 +05:30
mergify[bot]
33e8d05718 fix: Billing Address display in buying transactions (#35451)
fix: Billing Address display in buying transactions (#35451)

(cherry picked from commit bb21c044f6)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-05-31 11:03:49 +05:30
mergify[bot]
5fd00e7113 fix: Error while validating budget (#35487)
fix: Error while validating budget (#35487)

* fix: Error while validating budget

* chore: remove print statement

(cherry picked from commit 27d5e6a99b)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-05-31 11:02:14 +05:30
mergify[bot]
86801c29cb fix: incorrect transferred qty in the job card (#35478)
fix: incorrect transfer quantity in the job card

(cherry picked from commit d3a5e49db9)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2023-05-31 08:45:02 +05:30
Ankush Menat
c17e537bb6 chore: trigger ci 2023-05-30 19:00:40 +05:30
mergify[bot]
d231b19b9f fix: stock onboarding (backport #35453) (#35480)
* fix: stock settings tour

- remove dead fields
- Keep only essential fields in tour

(cherry picked from commit 2e13fbab5e)

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

* fix: hide ledger button on new warehouse

(cherry picked from commit 7a18db561f)

* fix: warehouse form cleanup

- organize fields
- group transit fields and move them lower
- enable/disable should be button
- hide pointless fields from listview

(cherry picked from commit 40ce33dff1)

* fix: disable/enable with button

(cherry picked from commit 81e901ba62)

* fix: warehouse tour

- remove warehouse type, it doesn't do what it says. Misleading.

(cherry picked from commit aa9f926298)

* fix: filter parent warehouses by company

(cherry picked from commit 3341cd6b80)

* fix: reorder stock reco tour

(cherry picked from commit dd245ccc7f)

* fix: replace stock projected with ledger

Ledger is much more widely used report, better to show that first?

(cherry picked from commit 8fe8f80033)

* chore: docs for stock settings

[skip ci]

(cherry picked from commit 964bb1d948)

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

* chore: conflicts

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-05-30 18:42:07 +05:30
rohitwaghchaure
09d3a98873 Merge pull request #35466 from frappe/mergify/bp/version-14-hotfix/pr-35465
fix: rate not fetching properly for inter transfer purchase order (backport #35465)
2023-05-30 11:49:30 +05:30
Sagar Sharma
a6f334a15e Merge pull request #35467 from frappe/mergify/bp/version-14-hotfix/pr-35459
fix: retention stock entry: grab conversion factor from source (backport #35459)
2023-05-30 11:35:55 +05:30
Marc de Lima Lucio
bd75584c27 fix: retention stock entry: grab conversion factor from source
(cherry picked from commit 6954f538c9)
2023-05-30 05:36:33 +00:00
Rohit Waghchaure
7b75f454d3 fix: rate not fetching properly for inter transfer purchase order
(cherry picked from commit 2931c657f4)
2023-05-30 03:45:03 +00:00
Anand Baburajan
37d437a33c fix: monthly WDV depr schedule for existing assets [v14] (#35458)
* fix: monthly wdv depr schedule for existing assets

* fix: monthly wdv depr schedule for existing assets properly
2023-05-29 23:17:59 +05:30
mergify[bot]
11440cca4c fix: Show future payments in accounts receivable summary (#35416)
fix: Show future payments in accounts receivable summary (#35416)

(cherry picked from commit 3504bf7f62)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2023-05-29 09:52:48 +05:30
Sagar Sharma
3673104949 Merge pull request #35443 from frappe/mergify/bp/version-14-hotfix/pr-35437
fix: incorrect `POS Reserved Qty` in `Stock Projected Qty` Report (backport #35437)
2023-05-28 16:18:29 +05:30
Sagar Sharma
71e4f34b86 fix: incorrect POS Reserved Qty in Stock Projected Qty Report
(cherry picked from commit 027de41600)
2023-05-28 09:35:34 +00:00
rohitwaghchaure
0a9c764f31 Merge pull request #35436 from frappe/mergify/bp/version-14-hotfix/pr-35426
fix: incorrect available quantity in BIN (backport #35426)
2023-05-27 12:46:02 +05:30
Rohit Waghchaure
fe1e2fec7a fix: travis
(cherry picked from commit 718ad3f240)
2023-05-26 17:18:40 +00:00
Rohit Waghchaure
5a9452f4a3 fix: incorrect available quantity in BIN
(cherry picked from commit 9e5e2de5d5)
2023-05-26 17:18:39 +00:00
Sagar Sharma
e251180c1a Merge pull request #35427 from frappe/mergify/bp/version-14-hotfix/pr-35108
refactor: Packing Slip (backport #35108)
2023-05-26 00:55:06 +05:30
Sagar Sharma
f5b2b80707 chore: conflict 2023-05-26 00:26:14 +05:30
Sagar Sharma
b3da2f7c26 fix(patch): add patch to set packed_qty in draft DN
(cherry picked from commit 196e18187f)
2023-05-25 18:24:56 +00:00
s-aga-r
ca6607e800 refactor: use get_product_bundle_list() to get all product bundle at once
(cherry picked from commit bbcb65894b)
2023-05-25 18:24:56 +00:00
s-aga-r
70857bf157 test: add test case for packed qty validation on DN submit
(cherry picked from commit ba61292dfc)
2023-05-25 18:24:56 +00:00
s-aga-r
dd7e5e019f refactor: validate_packed_qty()
(cherry picked from commit 699532647d)
2023-05-25 18:24:55 +00:00
s-aga-r
7105648468 refactor(minor): use set_onload to get unpacked items details
(cherry picked from commit b0eb9ea7bd)
2023-05-25 18:24:55 +00:00
s-aga-r
1ead0a3fef test: add test cases for Packing Slip
(cherry picked from commit 7742c592c5)
2023-05-25 18:24:55 +00:00
s-aga-r
9854c84ad8 fix(ux): don't show Create > Packing Slip button if items are already packed
(cherry picked from commit da00fc0f16)
2023-05-25 18:24:55 +00:00
s-aga-r
4017342c15 fix(ux): remove Get Items button from Packing Slip
(cherry picked from commit 8d1bccada4)
2023-05-25 18:24:54 +00:00
s-aga-r
b96aa75ded fix(ux): get items on selecting DN in Packing Slip
(cherry picked from commit e75aa4e291)
2023-05-25 18:24:54 +00:00
s-aga-r
1412c63158 refactor: move js validations to py
(cherry picked from commit 269cc96c41)
2023-05-25 18:24:54 +00:00
s-aga-r
cc7e267c35 fix: validate Packing Slip Item Qty with DN Items
(cherry picked from commit 90701c7ae9)
2023-05-25 18:24:54 +00:00
s-aga-r
b4e481a390 fix: make DN item reference mandatory for Packing Slip Item
(cherry picked from commit 9e5b102768)
2023-05-25 18:24:54 +00:00
s-aga-r
5345ebe242 fix: Packing Slip Item Qty
(cherry picked from commit 372bce4567)
2023-05-25 18:24:54 +00:00
s-aga-r
19713f9f6f chore: enable no_copy for dn_detail and pi_detail in Packing Slip Item
(cherry picked from commit 0add90e7ec)
2023-05-25 18:24:53 +00:00
s-aga-r
0bed06284e fix: update Packed Qty in DN on submit and cancel of Packing Slip
(cherry picked from commit 77f1e8ce78)
2023-05-25 18:24:53 +00:00
s-aga-r
509b68404c feat: add field Packed Qty in Delivery Note Item and Packed Item
(cherry picked from commit e6fc281acf)

# Conflicts:
#	erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
2023-05-25 18:24:53 +00:00
s-aga-r
6f0c7cf9a4 fix: don't map items twice
* don't explicitly map Delivery Note Item custom fields to Packing Slip Item, get auto-mapped while mapping the doc.
* call Packing List `set_missing_values` after mapping the doc.
* refactor `get_recommended_case_no`, use `frappe.db.get_value` instead of `frappe.db.sql`.

(cherry picked from commit 75fe9dd3ea)
2023-05-25 18:24:53 +00:00
s-aga-r
c7628c98c5 fix: remove duplicate items validation
(cherry picked from commit ee9f97ca7c)
2023-05-25 18:24:52 +00:00
s-aga-r
0c7efae858 refactor: packing_slip.js
(cherry picked from commit b62bf78814)
2023-05-25 18:24:52 +00:00
s-aga-r
984e32c34a fix: map Packed Items while creating Packing Slip
(cherry picked from commit 380dd73065)
2023-05-25 18:24:52 +00:00
s-aga-r
2b75474649 feat: add field pi_detail in Packing Slip
(cherry picked from commit eca77134ae)
2023-05-25 18:24:52 +00:00
Sagar Sharma
eb5cc2cf66 Merge pull request #35424 from frappe/mergify/bp/version-14-hotfix/pr-35422
chore: typo in stock balance (backport #35422)
2023-05-25 17:23:18 +05:30
Sagar Sharma
10a1681e87 chore: typo in stock balance
(cherry picked from commit c4e1f927ee)
2023-05-25 11:52:50 +00:00
ruthra kumar
54fac5786a Merge pull request #35419 from frappe/mergify/bp/version-14-hotfix/pr-35417
fix(Gross Profit): 'company' column is ambiguous in filter (backport #35417)
2023-05-25 16:44:45 +05:30
ruthra kumar
a59c205d2e fix(Gross Profit): 'company' column is ambiguous in filter
(cherry picked from commit 448712f719)
2023-05-25 09:37:24 +00:00
Deepesh Garg
151b599808 Merge pull request #35320 from akurungadam/gross-and-net-profit-fix
fix: Gross and Net Profit Report - incorrect calculation of totals
2023-05-25 14:43:10 +05:30
Frappe PR Bot
eb723637d3 chore(release): Bumped to Version 14.25.1
## [14.25.1](https://github.com/frappe/erpnext/compare/v14.25.0...v14.25.1) (2023-05-25)

### Bug Fixes

* Negative value in Reserved Qty for Production Plan ([20ceb6c](20ceb6c617))
2023-05-25 06:54:14 +00:00
rohitwaghchaure
aba322e086 Merge pull request #35414 from frappe/mergify/bp/version-14/pr-35411
fix: Negative value in Reserved Qty for Production Plan (backport #35410) (backport #35411)
2023-05-25 12:21:59 +05:30
Rohit Waghchaure
20ceb6c617 fix: Negative value in Reserved Qty for Production Plan
(cherry picked from commit a37608a36c)
(cherry picked from commit 6fe42c937c)
2023-05-25 04:37:18 +00:00
rohitwaghchaure
591b7705ac Merge pull request #35411 from frappe/mergify/bp/version-14-hotfix/pr-35410
fix: Negative value in Reserved Qty for Production Plan (backport #35410)
2023-05-24 22:57:21 +05:30
rohitwaghchaure
7163d7d27f Merge pull request #35413 from frappe/mergify/bp/version-14-hotfix/pr-35348
refactor: stock balance report (backport #35348)
2023-05-24 22:56:59 +05:30
Rohit Waghchaure
205899348a fix: Stock Analytics and Warehouse wise Item Balance Age and Value issue
(cherry picked from commit 3f548ac910)
2023-05-24 14:16:56 +00:00
Rohit Waghchaure
56ba7d6a8a fix: balance quantity
(cherry picked from commit 545b2d32cd)
2023-05-24 14:16:56 +00:00
Rohit Waghchaure
9fcfab219c refactor: stock balance report
(cherry picked from commit d9979b2ffb)
2023-05-24 14:16:56 +00:00
Rohit Waghchaure
6fe42c937c fix: Negative value in Reserved Qty for Production Plan
(cherry picked from commit a37608a36c)
2023-05-24 13:39:03 +00:00
rohitwaghchaure
1fe22f0fcf Merge pull request #35406 from frappe/mergify/bp/version-14-hotfix/pr-35405
fix: available qty not fetching for raw material in PP (backport #35405)
2023-05-24 17:29:39 +05:30
Rohit Waghchaure
746a734257 fix: available qty not fetching for raw material in PP
(cherry picked from commit 8e3463c4ef)
2023-05-24 08:55:40 +00:00
mergify[bot]
989052c075 fix: tab-uniformity (backport #35400) (#35402)
fix: tab-uniformity (#35400)

Made Doctype tabs uniform

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

Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com>
2023-05-24 12:47:38 +05:30
Frappe PR Bot
cd3991dd14 chore(release): Bumped to Version 14.25.0
# [14.25.0](https://github.com/frappe/erpnext/compare/v14.24.3...v14.25.0) (2023-05-24)

### Bug Fixes

* consider 0 if rate/qty are null (backport [#35338](https://github.com/frappe/erpnext/issues/35338)) ([#35340](https://github.com/frappe/erpnext/issues/35340)) ([15c1af3](15c1af3d8a))
* Creating landed cost voucher from connections ([2631224](2631224e49))
* depreciation schedule for existing assets [v14] ([#35255](https://github.com/frappe/erpnext/issues/35255)) ([0a080ef](0a080efce2))
* don't recalculate rate for SCR rejected warehouse SLE ([633a170](633a1703dc))
* error while saving job card ([fb7d3b7](fb7d3b7878))
* incorrect depr schedule and posting dates on selling of existing assets [v14] ([#35396](https://github.com/frappe/erpnext/issues/35396)) ([8af6a11](8af6a113d1))
* Pick List Status ([4888436](48884366ea))
* Pick List TypeError ([6df9b53](6df9b53682))
* possible type error on quotation -> sales order creation ([d23b93a](d23b93a462))
* replace quotation with invoice in first onboarding (backport [#35389](https://github.com/frappe/erpnext/issues/35389)) ([#35394](https://github.com/frappe/erpnext/issues/35394)) ([9b9772e](9b9772eb14))
* tds incorrectly calculated for invoice that are below threshold ([bdf81a4](bdf81a43c6))
* **test:** cumulative threshold checks ([879946e](879946e2c8))
* use flt instead of mandatory field ([668b092](668b092f6b))
* **ux:** SCR consumed-qty read-only property ([aa7fede](aa7fede0dc))

### Features

* provision to make reposting entries from Stock and Account Value Comparison Report ([3a0cdf3](3a0cdf30ce))
* provision to skip available sub assembly items in the production plan ([ce601af](ce601afc4e))
2023-05-24 03:05:06 +00:00
Deepesh Garg
e586c07bdd Merge pull request #35392 from frappe/version-14-hotfix
chore: release v14
2023-05-24 08:31:31 +05:30
Anand Baburajan
8af6a113d1 fix: incorrect depr schedule and posting dates on selling of existing assets [v14] (#35396)
* fix: use date in asset get gl entries functions

* fix: calc depr amount properly on selling of existing assets

* fix: calc depr amount properly on selling of existing assets again

* chore: remove unnecessary line breaks
2023-05-23 23:04:04 +05:30
mergify[bot]
9b9772eb14 fix: replace quotation with invoice in first onboarding (backport #35389) (#35394)
fix: replace quotation with invoice in first onboarding (#35389)

(cherry picked from commit b0eb72ffac)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-05-23 15:36:56 +05:30
Sagar Sharma
88ecc933f7 Merge pull request #35390 from frappe/mergify/bp/version-14-hotfix/pr-35303
fix: Pick List TypeError (backport #35303)
2023-05-23 15:06:05 +05:30
Sagar Sharma
6df9b53682 fix: Pick List TypeError
(cherry picked from commit a111917114)
2023-05-23 08:51:32 +00:00
rohitwaghchaure
47e97e0b3d Merge pull request #35382 from frappe/mergify/bp/version-14-hotfix/pr-35381
feat: provision to skip available sub assembly items in the production plan (backport #35381)
2023-05-23 12:29:18 +05:30
Sagar Sharma
fec6e3724e Merge pull request #35383 from frappe/mergify/bp/version-14-hotfix/pr-35380
fix: TypeError while saving Job card (backport #35380)
2023-05-23 10:52:00 +05:30
vishnu
668b092f6b fix: use flt instead of mandatory field
(cherry picked from commit 8c34cc0e00)
2023-05-23 05:20:22 +00:00
vishnu
fb7d3b7878 fix: error while saving job card
(cherry picked from commit a209fb4b64)
2023-05-23 05:20:22 +00:00
Rohit Waghchaure
f6857b4698 test: test case to skip available qty for sub-assembly items
(cherry picked from commit 9dd566c1e8)
2023-05-23 04:09:40 +00:00
Rohit Waghchaure
ce601afc4e feat: provision to skip available sub assembly items in the production plan
(cherry picked from commit 64751ec4d9)
2023-05-23 04:09:40 +00:00
Anoop
5b4b71d941 Merge branch 'version-14-hotfix' into gross-and-net-profit-fix 2023-05-22 17:23:03 +05:30
Sagar Sharma
1b827f61f7 Merge pull request #35377 from frappe/mergify/bp/version-14-hotfix/pr-35376
fix: don't recalculate rate for SCR rejected warehouse SLE (backport #35376)
2023-05-22 16:08:25 +05:30
Sagar Sharma
633a1703dc fix: don't recalculate rate for SCR rejected warehouse SLE
(cherry picked from commit 57ee473fa4)
2023-05-22 10:16:09 +00:00
Ankush Menat
abfd975eb6 chore: drop outdated navigation video
new stuff is in work, this one is actually counter-productive rn.

(cherry picked from commit ba61865ee6)
2023-05-20 19:47:17 +05:30
rohitwaghchaure
9106b01c04 Merge pull request #35368 from frappe/mergify/bp/version-14-hotfix/pr-35365
feat: provision to make reposting entries from Stock and Account Value Comparison Report (backport #35365)
2023-05-20 18:31:00 +05:30
Rohit Waghchaure
3a0cdf30ce feat: provision to make reposting entries from Stock and Account Value Comparison Report
(cherry picked from commit 7b818e9d34)
2023-05-20 11:37:02 +00:00
ruthra kumar
4620ed6e42 refactor: replace join with sub-query for fetching accounts
(cherry picked from commit e6a9252f79)
2023-05-19 05:32:43 +00:00
Frappe PR Bot
756ac6c587 chore(release): Bumped to Version 14.24.3
## [14.24.3](https://github.com/frappe/erpnext/compare/v14.24.2...v14.24.3) (2023-05-18)

### Bug Fixes

* tds incorrectly calculated for invoice that are below threshold ([14565ed](14565ed8b1))
* **test:** cumulative threshold checks ([cdd378c](cdd378c518))
2023-05-18 14:07:04 +00:00
ruthra kumar
b5b1b2da32 Merge pull request #35359 from frappe/mergify/bp/version-14/pr-35335
fix: tds incorrectly calculated for invoice that are below threshold (backport #35335)
2023-05-18 19:35:11 +05:30
ruthra kumar
cdd378c518 fix(test): cumulative threshold checks
(cherry picked from commit 132846bbd1)
2023-05-18 13:30:03 +00:00
ruthra kumar
14565ed8b1 fix: tds incorrectly calculated for invoice that are below threshold
Two purchase invoices for the same supplier, using different tax
withholding categories have this issue.

| Category | single | cumulative |
|----------+--------+------------|
| cat1     |    100 |        500 |
| cat2     |   1000 |       5000 |

1. PINV1 of net total: 105/- uses cat1. TDS is calculated as it
breached single threshold
2. PINV2 of net total: 200/- uses cat2. TDS incorrectly calculated as
PINV1 already has TDS calculated and 'consider_party_ledger_amount' is enabled.

(cherry picked from commit 84b7c1bba0)
2023-05-18 13:30:02 +00:00
ruthra kumar
44b290f58e Merge pull request #35357 from frappe/mergify/bp/version-14-hotfix/pr-35355
fix: possible type error on quotation -> sales order creation (backport #35355)
2023-05-18 14:35:53 +05:30
ruthra kumar
d23b93a462 fix: possible type error on quotation -> sales order creation
(cherry picked from commit b2290c6f57)
2023-05-18 13:39:58 +05:30
ruthra kumar
80f4a11d60 Merge pull request #35353 from frappe/mergify/bp/version-14-hotfix/pr-35335
fix: tds incorrectly calculated for invoice that are below threshold (backport #35335)
2023-05-18 13:03:40 +05:30
Sagar Sharma
9caa39195c Merge pull request #35344 from frappe/mergify/bp/version-14-hotfix/pr-35306
fix: Pick List Status (backport #35306)
2023-05-18 12:52:25 +05:30
ruthra kumar
879946e2c8 fix(test): cumulative threshold checks
(cherry picked from commit 132846bbd1)
2023-05-18 07:09:20 +00:00
ruthra kumar
bdf81a43c6 fix: tds incorrectly calculated for invoice that are below threshold
Two purchase invoices for the same supplier, using different tax
withholding categories have this issue.

| Category | single | cumulative |
|----------+--------+------------|
| cat1     |    100 |        500 |
| cat2     |   1000 |       5000 |

1. PINV1 of net total: 105/- uses cat1. TDS is calculated as it
breached single threshold
2. PINV2 of net total: 200/- uses cat2. TDS incorrectly calculated as
PINV1 already has TDS calculated and 'consider_party_ledger_amount' is enabled.

(cherry picked from commit 84b7c1bba0)
2023-05-18 07:09:19 +00:00
Sagar Sharma
cb681e0b96 Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-35306 2023-05-18 12:30:40 +05:30
ruthra kumar
65554bdabf Merge pull request #35336 from frappe/mergify/bp/version-14-hotfix/pr-35239
fix: in payment_entry  difference amount cal is broken (backport #35239)
2023-05-18 12:15:44 +05:30
rohitwaghchaure
0d2d45f32f Merge pull request #35351 from frappe/mergify/bp/version-14-hotfix/pr-35298
fix: Creating landed cost voucher from connections (backport #35298)
2023-05-18 06:16:11 +05:30
vishnu
2631224e49 fix: Creating landed cost voucher from connections
(cherry picked from commit f2ceb00379)
2023-05-18 00:43:36 +00:00
Anand Baburajan
0a080efce2 fix: depreciation schedule for existing assets [v14] (#35255)
* fix: depreciation schedule for existing assets

* chore: correct logic for existing assets and fix test
2023-05-17 22:21:00 +05:30
Sagar Sharma
48884366ea fix: Pick List Status
(cherry picked from commit 9fb8b1827d)
2023-05-17 12:43:08 +00:00
mergify[bot]
15c1af3d8a fix: consider 0 if rate/qty are null (backport #35338) (#35340)
fix: consider 0 if rate/qty are null (#35338)

[skip ci]

(cherry picked from commit e5c86bc2e8)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-05-17 16:17:21 +05:30
Sagar Sharma
350f75877a Merge pull request #35339 from frappe/mergify/bp/version-14-hotfix/pr-35337
fix(ux): SCR consumed-qty read-only property (backport #35337)
2023-05-17 15:58:29 +05:30
Sagar Sharma
aa7fede0dc fix(ux): SCR consumed-qty read-only property
(cherry picked from commit adf2474d9d)
2023-05-17 10:28:03 +00:00
Ashish Shah
e9f2ab5caf refactor: use 'flt' for base_total_taxes_and_charges
difference_amount calculation is broken, as calculation gives NaN. Fix is make frm.doc.base_total_taxes_and_charges as flt(frm.doc.base_total_taxes_and_charges)

(cherry picked from commit ae4e56747c)
2023-05-17 09:40:55 +00:00
Frappe PR Bot
7076c23a4f chore(release): Bumped to Version 14.24.2
## [14.24.2](https://github.com/frappe/erpnext/compare/v14.24.1...v14.24.2) (2023-05-16)

### Bug Fixes

* add missing options for `Content Align` ([3697e8f](3697e8f1f9))
* allow over-payment against SO ([#35079](https://github.com/frappe/erpnext/issues/35079)) ([fe9e0c2](fe9e0c2121))
* bad strings format for command get-untraslated  ([#34361](https://github.com/frappe/erpnext/issues/34361)) ([5a54296](5a54296686))
* bad strings format for update-translations ([#34592](https://github.com/frappe/erpnext/issues/34592)) ([e3c1d73](e3c1d736ce))
* bom item filter issue ([098603d](098603dd35))
* cancelled vouchers in tax withheld vouchers list ([#35309](https://github.com/frappe/erpnext/issues/35309)) ([c41e1d7](c41e1d7d71))
* enqueue submit/cancel action for stock entry to avoid time out error ([457846e](457846e34c))
* force to do reposting for cancelled document ([6f96e5d](6f96e5dcd4))
* function `batch_no` should only be declared once ([#35115](https://github.com/frappe/erpnext/issues/35115)) ([26928b3](26928b395b))
* incorrect packing items ([ab56470](ab56470171))
* inventory dimension for inter company transfer return use case ([6d121b8](6d121b8107))
* inventory dimension for material transfer not working ([1d8050d](1d8050d24d))
* item list view not working ([d9efa66](d9efa662d4))
* port option for additional_conditions in item wise sales register ([#35187](https://github.com/frappe/erpnext/issues/35187)) ([42037f9](42037f9f73))
* recalculate costs in SCR while reposting ([9a8ee62](9a8ee62d5a))
* sales person allocated amount calculation error nonetype and float ([#35293](https://github.com/frappe/erpnext/issues/35293)) ([3a7c69f](3a7c69fc71))
* test case ([3f8928b](3f8928be5c))
* test case ([9b2b467](9b2b46737e))
* typo ([2772a91](2772a911ed))
* unable to create partial invoice with auto fetch terms enabled ([#35285](https://github.com/frappe/erpnext/issues/35285)) ([fa9fa97](fa9fa97e05))
* update workstation hour rate when workstation change in job card ([bc88415](bc88415e73))
* **UX:** misc "home" onboarding improvements (backport [#35319](https://github.com/frappe/erpnext/issues/35319)) ([#35321](https://github.com/frappe/erpnext/issues/35321)) ([f8c58b6](f8c58b6893))
* validate for active sla ([#32132](https://github.com/frappe/erpnext/issues/32132)) ([38e27a6](38e27a68d5))

### Performance Improvements

* cache and simplify queries for holiday list (backport [#35315](https://github.com/frappe/erpnext/issues/35315)) ([#35318](https://github.com/frappe/erpnext/issues/35318)) ([0e78403](0e7840301f))
2023-05-16 17:00:00 +00:00
Deepesh Garg
bdfd682664 Merge pull request #35324 from frappe/version-14-hotfix
chore: release v14
2023-05-16 22:28:33 +05:30
Anoop
fb0f82eed3 Merge branch 'version-14-hotfix' into gross-and-net-profit-fix 2023-05-16 20:10:23 +05:30
mergify[bot]
fa9fa97e05 fix: unable to create partial invoice with auto fetch terms enabled (#35285)
fix: unable to create partial invoice with auto fetch terms enabled (#35285)

fix: fetch so/po terms if auto fetch is enabled
(cherry picked from commit 0da6c1688b)

Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2023-05-16 18:59:12 +05:30
mergify[bot]
c41e1d7d71 fix: cancelled vouchers in tax withheld vouchers list (#35309)
fix: cancelled vouchers in tax withheld vouchers list (#35309)

(cherry picked from commit 776a83066d)

Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2023-05-16 18:58:04 +05:30
rohitwaghchaure
8f8c0a597b Merge pull request #35329 from frappe/mergify/bp/version-14-hotfix/pr-35328
fix: force to do reposting for cancelled document (backport #35328)
2023-05-16 18:53:33 +05:30
Rohit Waghchaure
6f96e5dcd4 fix: force to do reposting for cancelled document
(cherry picked from commit 6e661e7c0e)
2023-05-16 12:03:43 +00:00
mergify[bot]
f8c58b6893 fix(UX): misc "home" onboarding improvements (backport #35319) (#35321)
fix(UX): misc "home" onboarding improvements (#35319)

* fix(UX): cleanup "Home" onboarding

- Remove company, letterhead, data import etc from home onboarding step

* fix(UX): Show quick entry for item

* chore: fix copy here and there

(cherry picked from commit 5574d9a72d)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-05-16 16:00:25 +05:30
mergify[bot]
0e7840301f perf: cache and simplify queries for holiday list (backport #35315) (#35318)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2023-05-16 13:44:35 +05:30
Anoop Kurungadam
cb9b4fbb91 fix: add total col for gross and net profit 2023-05-16 12:08:42 +05:30
Anoop Kurungadam
1a3b9c5bdf refactor: merge separate loops for calculating group / leaf node totals
rename function
remove return statement as the list is  mutated
2023-05-16 12:08:32 +05:30
Anoop Kurungadam
50822f207e refactor: remove unused parameters 2023-05-16 12:08:24 +05:30
Anoop Kurungadam
8dcb9302b4 fix: account group totals calculation to consider include_in_gross 2023-05-16 12:08:17 +05:30
rohitwaghchaure
19f08afcef Merge pull request #35314 from frappe/mergify/bp/version-14-hotfix/pr-35312
fix: item list view not working (backport #35312)
2023-05-16 11:10:18 +05:30
mergify[bot]
42037f9f73 fix: port option for additional_conditions in item wise sales register (#35187)
fix: port option for additional_conditions in item wise sales register (#35187)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
(cherry picked from commit 2a609616d9)

Co-authored-by: Smit Vora <smitvora203@gmail.com>
2023-05-16 07:29:48 +05:30
Rohit Waghchaure
d9efa662d4 fix: item list view not working
(cherry picked from commit 0489e30244)
2023-05-15 19:40:23 +00:00
rohitwaghchaure
ee147e62d5 Merge pull request #35311 from CodeVenturers/version-14-hotfix
fix: update workstation hour rate when workstation change in job card
2023-05-16 00:44:26 +05:30
vishnu
bc88415e73 fix: update workstation hour rate when workstation change in job card 2023-05-15 14:50:40 +00:00
Sagar Sharma
5463f0b137 Merge pull request #35308 from frappe/mergify/bp/version-14-hotfix/pr-35138
fix: recalculate costs in SCR while reposting (backport #35138)
2023-05-15 17:14:57 +05:30
Sagar Sharma
a8fc17e0ae refactor: use calculate_items_qty_and_amount() to update scr items rate
(cherry picked from commit 9c72c2a6cb)
2023-05-15 10:56:51 +00:00
Sagar Sharma
0575b105d0 refactor(minor): rename function to be more descriptive
(cherry picked from commit d6433f803b)
2023-05-15 10:56:51 +00:00
Sagar Sharma
4c8dbeddec test: add test case
(cherry picked from commit e0b22edb2e)
2023-05-15 10:56:51 +00:00
s-aga-r
9a8ee62d5a fix: recalculate costs in SCR while reposting
(cherry picked from commit a6cb6c6f47)
2023-05-15 10:56:50 +00:00
mergify[bot]
3a7c69fc71 fix: sales person allocated amount calculation error nonetype and float (#35293)
fix: sales person allocated amount calculation error nonetype and float (#35293)

fix: sales person allocated amount calculation error nontype and float
(cherry picked from commit 0c8276ec82)

Co-authored-by: Indrajith.vs <91895505+Gubbu77@users.noreply.github.com>
2023-05-15 13:16:47 +05:30
rohitwaghchaure
dd116a3071 Merge pull request #35291 from frappe/mergify/bp/version-14-hotfix/pr-35289
fix: inventory dimension for returned inter company transfer (backport #35289)
2023-05-14 16:29:34 +05:30
rohitwaghchaure
3f8928be5c fix: test case 2023-05-14 15:49:58 +05:30
mergify[bot]
26928b395b fix: function batch_no should only be declared once (#35115)
fix: function `batch_no` should only be declared once (#35115)

fix: remove twice event call of `batch_no` to update batch qty
(cherry picked from commit 19cd687784)

Co-authored-by: Daizy Modi <modidaizy5217@gmail.com>
2023-05-14 11:54:15 +05:30
mergify[bot]
fe9e0c2121 fix: allow over-payment against SO (#35079)
fix: allow over-payment against SO (#35079)

(cherry picked from commit 870b02b03c)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-05-14 11:44:50 +05:30
Deepesh Garg
77cf2e5475 Merge pull request #35262 from akiratfli/fix-bad-string
fix: bad strings format for translations
2023-05-14 08:06:24 +05:30
rohitwaghchaure
2772a911ed fix: typo 2023-05-13 15:14:41 +05:30
Rohit Waghchaure
6d121b8107 fix: inventory dimension for inter company transfer return use case
(cherry picked from commit 38aaba5720)
2023-05-13 08:57:03 +00:00
Sagar Sharma
c30dda3328 Merge pull request #35283 from frappe/mergify/bp/version-14-hotfix/pr-35275
fix: add missing options for `Content Align` (backport #35275)
2023-05-13 09:42:20 +05:30
Sagar Sharma
b2e6fdc0cb Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-35275 2023-05-13 09:41:30 +05:30
rohitwaghchaure
75af689f77 Merge pull request #35282 from frappe/mergify/bp/version-14-hotfix/pr-35224
fix: inventory dimension for material transfer not working (backport #35224)
2023-05-13 09:36:42 +05:30
Sagar Sharma
3697e8f1f9 fix: add missing options for Content Align
(cherry picked from commit d16caa2d2c)
2023-05-13 04:05:15 +00:00
Rohit Waghchaure
1d8050d24d fix: inventory dimension for material transfer not working
(cherry picked from commit 6798b900ef)
2023-05-12 20:46:04 +00:00
rohitwaghchaure
4206f01c05 Merge pull request #35274 from frappe/mergify/bp/version-14-hotfix/pr-35273
fix: incorrect packing items (backport #35273)
2023-05-12 15:06:25 +05:30
Rohit Waghchaure
ab56470171 fix: incorrect packing items
(cherry picked from commit a686b8c337)
2023-05-12 09:13:52 +00:00
rohitwaghchaure
8af1e49e96 Merge pull request #35269 from frappe/mergify/bp/version-14-hotfix/pr-35268
fix: BOM item filter issue (backport #35268)
2023-05-12 13:12:11 +05:30
rohitwaghchaure
19f0676592 Merge pull request #35266 from frappe/mergify/bp/version-14-hotfix/pr-35261
fix: enqueue submit/cancel action for stock entry to avoid time out error (backport #35261)
2023-05-12 13:10:48 +05:30
Rohit Waghchaure
098603dd35 fix: bom item filter issue
(cherry picked from commit 2879cb7c28)
2023-05-12 07:40:38 +00:00
Rohit Waghchaure
9b2b46737e fix: test case
(cherry picked from commit 2d6f112727)
2023-05-12 07:03:22 +00:00
Rohit Waghchaure
457846e34c fix: enqueue submit/cancel action for stock entry to avoid time out error
(cherry picked from commit 7a3801578c)
2023-05-12 07:03:21 +00:00
Danny
5a54296686 fix: bad strings format for command get-untraslated (#34361)
(cherry picked from commit ca10e2bb9f)
2023-05-12 03:19:01 +00:00
justin.li
e3c1d736ce fix: bad strings format for update-translations (#34592)
(cherry picked from commit 07c9b99072)
2023-05-12 02:21:19 +00:00
Saqib Ansari
c8e3ce48e1 Merge pull request #35257 from frappe/mergify/bp/version-14-hotfix/pr-32132
fix: validate for active sla (backport #32132)
2023-05-11 17:50:00 +05:30
Shadrak Gurupnor
38e27a68d5 fix: validate for active sla (#32132)
(cherry picked from commit f2b7c9ee66)
2023-05-11 12:18:49 +00:00
167 changed files with 5056 additions and 2320 deletions

2
.github/stale.yml vendored
View File

@@ -13,7 +13,7 @@ exemptProjects: true
exemptMilestones: true
pulls:
daysUntilStale: 15
daysUntilStale: 14
daysUntilClose: 3
exemptLabels:
- hotfix

View File

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

View File

@@ -50,13 +50,15 @@ class AccountingDimension(Document):
if frappe.flags.in_test:
make_dimension_in_accounting_doctypes(doc=self)
else:
frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self, queue="long")
frappe.enqueue(
make_dimension_in_accounting_doctypes, doc=self, queue="long", enqueue_after_commit=True
)
def on_trash(self):
if frappe.flags.in_test:
delete_accounting_dimension(doc=self)
else:
frappe.enqueue(delete_accounting_dimension, doc=self, queue="long")
frappe.enqueue(delete_accounting_dimension, doc=self, queue="long", enqueue_after_commit=True)
def set_fieldname_and_label(self):
if not self.label:

View File

@@ -36,6 +36,7 @@
"book_tax_discount_loss",
"print_settings",
"show_inclusive_tax_in_print",
"show_taxes_as_table_in_print",
"column_break_12",
"show_payment_schedule_in_print",
"currency_exchange_section",
@@ -378,6 +379,12 @@
"fieldname": "auto_reconcile_payments",
"fieldtype": "Check",
"label": "Auto Reconcile Payments"
},
{
"default": "0",
"fieldname": "show_taxes_as_table_in_print",
"fieldtype": "Check",
"label": "Show Taxes as Table in Print"
}
],
"icon": "icon-cog",
@@ -385,7 +392,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-04-21 13:11:37.130743",
"modified": "2023-06-13 18:47:46.430291",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
@@ -414,4 +421,4 @@
"sort_order": "ASC",
"states": [],
"track_changes": 1
}
}

View File

@@ -46,7 +46,7 @@ class BankTransaction(StatusUpdater):
def add_payment_entries(self, vouchers):
"Add the vouchers with zero allocation. Save() will perform the allocations and clearance"
if 0.0 >= self.unallocated_amount:
frappe.throw(frappe._(f"Bank Transaction {self.name} is already fully reconciled"))
frappe.throw(frappe._("Bank Transaction {0} is already fully reconciled").format(self.name))
added = False
for voucher in vouchers:
@@ -114,9 +114,7 @@ class BankTransaction(StatusUpdater):
elif 0.0 > unallocated_amount:
self.db_delete_payment_entry(payment_entry)
frappe.throw(
frappe._(f"Voucher {payment_entry.payment_entry} is over-allocated by {unallocated_amount}")
)
frappe.throw(frappe._("Voucher {0} is over-allocated by {1}").format(unallocated_amount))
self.reload()
@@ -178,7 +176,9 @@ def get_clearance_details(transaction, payment_entry):
if gle["gl_account"] == gl_bank_account:
if gle["amount"] <= 0.0:
frappe.throw(
frappe._(f"Voucher {payment_entry.payment_entry} value is broken: {gle['amount']}")
frappe._("Voucher {0} value is broken: {1}").format(
payment_entry.payment_entry, gle["amount"]
)
)
unmatched_gles -= 1

View File

@@ -125,14 +125,27 @@ def validate_expense_against_budget(args, expense_amount=0):
if not args.account:
return
for budget_against in ["project", "cost_center"] + get_accounting_dimensions():
default_dimensions = [
{
"fieldname": "project",
"document_type": "Project",
},
{
"fieldname": "cost_center",
"document_type": "Cost Center",
},
]
for dimension in default_dimensions + get_accounting_dimensions(as_list=False):
budget_against = dimension.get("fieldname")
if (
args.get(budget_against)
and args.account
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})
):
doctype = frappe.unscrub(budget_against)
doctype = dimension.get("document_type")
if frappe.get_cached_value("DocType", doctype, "is_tree"):
lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"])

View File

@@ -35,6 +35,21 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
}
},
validate_rounding_loss: function(frm) {
let allowance = frm.doc.rounding_loss_allowance;
if (!(allowance > 0 && allowance < 1)) {
frappe.throw(__("Rounding Loss Allowance should be between 0 and 1"));
}
},
rounding_loss_allowance: function(frm) {
frm.events.validate_rounding_loss(frm);
},
validate: function(frm) {
frm.events.validate_rounding_loss(frm);
},
get_entries: function(frm, account) {
frappe.call({
method: "get_accounts_data",
@@ -126,7 +141,8 @@ var get_account_details = function(frm, cdt, cdn) {
company: frm.doc.company,
posting_date: frm.doc.posting_date,
party_type: row.party_type,
party: row.party
party: row.party,
rounding_loss_allowance: frm.doc.rounding_loss_allowance
},
callback: function(r){
$.extend(row, r.message);

View File

@@ -8,6 +8,7 @@
"engine": "InnoDB",
"field_order": [
"posting_date",
"rounding_loss_allowance",
"column_break_2",
"company",
"section_break_4",
@@ -96,11 +97,18 @@
{
"fieldname": "column_break_10",
"fieldtype": "Column Break"
},
{
"default": "0.05",
"description": "Only values between 0 and 1 are allowed. \nEx: If allowance is set at 0.07, accounts that have balance of 0.07 in either of the currencies will be considered as zero balance account",
"fieldname": "rounding_loss_allowance",
"fieldtype": "Float",
"label": "Rounding Loss Allowance"
}
],
"is_submittable": 1,
"links": [],
"modified": "2022-12-29 19:38:24.416529",
"modified": "2023-06-12 21:02:09.818208",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Exchange Rate Revaluation",

View File

@@ -12,13 +12,19 @@ from frappe.utils import flt, get_link_to_form
import erpnext
from erpnext.accounts.doctype.journal_entry.journal_entry import get_balance_on
from erpnext.accounts.utils import get_currency_precision
from erpnext.setup.utils import get_exchange_rate
class ExchangeRateRevaluation(Document):
def validate(self):
self.validate_rounding_loss_allowance()
self.set_total_gain_loss()
def validate_rounding_loss_allowance(self):
if not (self.rounding_loss_allowance > 0 and self.rounding_loss_allowance < 1):
frappe.throw(_("Rounding Loss Allowance should be between 0 and 1"))
def set_total_gain_loss(self):
total_gain_loss = 0
@@ -91,7 +97,12 @@ class ExchangeRateRevaluation(Document):
def get_accounts_data(self):
self.validate_mandatory()
account_details = self.get_account_balance_from_gle(
company=self.company, posting_date=self.posting_date, account=None, party_type=None, party=None
company=self.company,
posting_date=self.posting_date,
account=None,
party_type=None,
party=None,
rounding_loss_allowance=self.rounding_loss_allowance,
)
accounts_with_new_balance = self.calculate_new_account_balance(
self.company, self.posting_date, account_details
@@ -103,7 +114,9 @@ class ExchangeRateRevaluation(Document):
return accounts_with_new_balance
@staticmethod
def get_account_balance_from_gle(company, posting_date, account, party_type, party):
def get_account_balance_from_gle(
company, posting_date, account, party_type, party, rounding_loss_allowance
):
account_details = []
if company and posting_date:
@@ -170,6 +183,23 @@ class ExchangeRateRevaluation(Document):
.run(as_dict=True)
)
# round off balance based on currency precision
# and consider debit-credit difference allowance
currency_precision = get_currency_precision()
rounding_loss_allowance = float(rounding_loss_allowance) or 0.05
for acc in account_details:
acc.balance_in_account_currency = flt(acc.balance_in_account_currency, currency_precision)
if abs(acc.balance_in_account_currency) <= rounding_loss_allowance:
acc.balance_in_account_currency = 0
acc.balance = flt(acc.balance, currency_precision)
if abs(acc.balance) <= rounding_loss_allowance:
acc.balance = 0
acc.zero_balance = (
True if (acc.balance == 0 or acc.balance_in_account_currency == 0) else False
)
return account_details
@staticmethod
@@ -521,7 +551,9 @@ def calculate_exchange_rate_using_last_gle(company, account, party_type, party):
@frappe.whitelist()
def get_account_details(company, posting_date, account, party_type=None, party=None):
def get_account_details(
company, posting_date, account, party_type=None, party=None, rounding_loss_allowance: float = None
):
if not (company and posting_date):
frappe.throw(_("Company and Posting Date is mandatory"))
@@ -539,7 +571,12 @@ def get_account_details(company, posting_date, account, party_type=None, party=N
"account_currency": account_currency,
}
account_balance = ExchangeRateRevaluation.get_account_balance_from_gle(
company=company, posting_date=posting_date, account=account, party_type=party_type, party=party
company=company,
posting_date=posting_date,
account=account,
party_type=party_type,
party=party,
rounding_loss_allowance=rounding_loss_allowance,
)
if account_balance and (

View File

@@ -940,6 +940,7 @@ class JournalEntry(AccountsController):
blank_row.debit_in_account_currency = abs(diff)
blank_row.debit = abs(diff)
self.set_total_debit_credit()
self.validate_total_debit_and_credit()
@frappe.whitelist()

View File

@@ -905,7 +905,7 @@ frappe.ui.form.on('Payment Entry', {
function(d) { return flt(d.amount) }));
frm.set_value("difference_amount", difference_amount - total_deductions +
frm.doc.base_total_taxes_and_charges);
flt(frm.doc.base_total_taxes_and_charges));
frm.events.hide_unhide_fields(frm);
},

View File

@@ -148,19 +148,57 @@ class PaymentEntry(AccountsController):
)
def validate_allocated_amount(self):
for d in self.get("references"):
if self.payment_type == "Internal Transfer":
return
latest_references = get_outstanding_reference_documents(
{
"posting_date": self.posting_date,
"company": self.company,
"party_type": self.party_type,
"payment_type": self.payment_type,
"party": self.party,
"party_account": self.paid_from if self.payment_type == "Receive" else self.paid_to,
}
)
# Group latest_references by (voucher_type, voucher_no)
latest_lookup = {}
for d in latest_references:
d = frappe._dict(d)
latest_lookup.update({(d.voucher_type, d.voucher_no): d})
for d in self.get("references").copy():
latest = latest_lookup.get((d.reference_doctype, d.reference_name))
# The reference has already been fully paid
if not latest:
frappe.throw(
_("{0} {1} has already been fully paid.").format(d.reference_doctype, d.reference_name)
)
# The reference has already been partly paid
elif (
latest.outstanding_amount < latest.invoice_amount
and d.outstanding_amount != latest.outstanding_amount
):
frappe.throw(
_(
"{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' button to get the latest outstanding amount."
).format(d.reference_doctype, d.reference_name)
)
d.outstanding_amount = latest.outstanding_amount
fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.")
if (flt(d.allocated_amount)) > 0:
if flt(d.allocated_amount) > flt(d.outstanding_amount):
frappe.throw(
_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx)
)
frappe.throw(fail_message.format(d.idx))
# Check for negative outstanding invoices as well
if flt(d.allocated_amount) < 0:
if flt(d.allocated_amount) < flt(d.outstanding_amount):
frappe.throw(
_("Row #{0}: Allocated Amount cannot be greater than outstanding amount.").format(d.idx)
)
frappe.throw(fail_message.format(d.idx))
def delink_advance_entry_references(self):
for reference in self.references:
@@ -373,7 +411,7 @@ class PaymentEntry(AccountsController):
for k, v in no_oustanding_refs.items():
frappe.msgprint(
_(
"{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry."
"{} - {} now has {} as it had no outstanding amount left before submitting the Payment Entry."
).format(
_(k),
frappe.bold(", ".join(d.reference_name for d in v)),
@@ -1449,7 +1487,7 @@ def get_orders_to_be_billed(
if voucher_type:
doc = frappe.get_doc({"doctype": voucher_type})
condition = ""
if doc and hasattr(doc, "cost_center"):
if doc and hasattr(doc, "cost_center") and doc.cost_center:
condition = " and cost_center='%s'" % cost_center
orders = []
@@ -1495,9 +1533,15 @@ def get_orders_to_be_billed(
order_list = []
for d in orders:
if not (
flt(d.outstanding_amount) >= flt(filters.get("outstanding_amt_greater_than"))
and flt(d.outstanding_amount) <= flt(filters.get("outstanding_amt_less_than"))
if (
filters
and filters.get("outstanding_amt_greater_than")
and filters.get("outstanding_amt_less_than")
and not (
flt(filters.get("outstanding_amt_greater_than"))
<= flt(d.outstanding_amount)
<= flt(filters.get("outstanding_amt_less_than"))
)
):
continue

View File

@@ -1013,6 +1013,30 @@ class TestPaymentEntry(FrappeTestCase):
employee = make_employee("test_payment_entry@salary.com", company="_Test Company")
create_payment_entry(party_type="Employee", party=employee, save=True)
def test_duplicate_payment_entry_allocate_amount(self):
si = create_sales_invoice()
pe_draft = get_payment_entry("Sales Invoice", si.name)
pe_draft.insert()
pe = get_payment_entry("Sales Invoice", si.name)
pe.submit()
self.assertRaises(frappe.ValidationError, pe_draft.submit)
def test_duplicate_payment_entry_partial_allocate_amount(self):
si = create_sales_invoice()
pe_draft = get_payment_entry("Sales Invoice", si.name)
pe_draft.insert()
pe = get_payment_entry("Sales Invoice", si.name)
pe.received_amount = si.total / 2
pe.references[0].allocated_amount = si.total / 2
pe.submit()
self.assertRaises(frappe.ValidationError, pe_draft.submit)
def create_payment_entry(**args):
payment_entry = frappe.new_doc("Payment Entry")

View File

@@ -6,7 +6,6 @@ import frappe
from frappe import _, msgprint, qb
from frappe.model.document import Document
from frappe.query_builder.custom import ConstantColumn
from frappe.query_builder.functions import IfNull
from frappe.utils import flt, get_link_to_form, getdate, nowdate, today
import erpnext
@@ -127,12 +126,29 @@ class PaymentReconciliation(Document):
return list(journal_entries)
def get_return_invoices(self):
voucher_type = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice"
doc = qb.DocType(voucher_type)
self.return_invoices = (
qb.from_(doc)
.select(
ConstantColumn(voucher_type).as_("voucher_type"),
doc.name.as_("voucher_no"),
doc.return_against,
)
.where(
(doc.docstatus == 1)
& (doc[frappe.scrub(self.party_type)] == self.party)
& (doc.is_return == 1)
)
.run(as_dict=True)
)
def get_dr_or_cr_notes(self):
self.build_qb_filter_conditions(get_return_invoices=True)
ple = qb.DocType("Payment Ledger Entry")
voucher_type = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice"
if erpnext.get_party_account_type(self.party_type) == "Receivable":
self.common_filter_conditions.append(ple.account_type == "Receivable")
@@ -140,19 +156,10 @@ class PaymentReconciliation(Document):
self.common_filter_conditions.append(ple.account_type == "Payable")
self.common_filter_conditions.append(ple.account == self.receivable_payable_account)
# get return invoices
doc = qb.DocType(voucher_type)
return_invoices = (
qb.from_(doc)
.select(ConstantColumn(voucher_type).as_("voucher_type"), doc.name.as_("voucher_no"))
.where(
(doc.docstatus == 1)
& (doc[frappe.scrub(self.party_type)] == self.party)
& (doc.is_return == 1)
& (IfNull(doc.return_against, "") == "")
)
.run(as_dict=True)
)
self.get_return_invoices()
return_invoices = [
x for x in self.return_invoices if x.return_against == None or x.return_against == ""
]
outstanding_dr_or_cr = []
if return_invoices:
@@ -204,6 +211,15 @@ class PaymentReconciliation(Document):
accounting_dimensions=self.accounting_dimension_filter_conditions,
)
cr_dr_notes = (
[x.voucher_no for x in self.return_invoices]
if self.party_type in ["Customer", "Supplier"]
else []
)
# Filter out cr/dr notes from outstanding invoices list
# Happens when non-standalone cr/dr notes are linked with another invoice through journal entry
non_reconciled_invoices = [x for x in non_reconciled_invoices if x.voucher_no not in cr_dr_notes]
if self.invoice_limit:
non_reconciled_invoices = non_reconciled_invoices[: self.invoice_limit]

View File

@@ -35,6 +35,7 @@ class PeriodClosingVoucher(AccountsController):
voucher_type="Period Closing Voucher",
voucher_no=self.name,
queue="long",
enqueue_after_commit=True,
)
frappe.msgprint(
_("The GL Entries will be cancelled in the background, it can take a few minutes."), alert=True
@@ -169,21 +170,18 @@ class PeriodClosingVoucher(AccountsController):
return frappe.db.sql(
"""
select
t2.account_currency,
t1.account_currency,
{dimension_fields},
sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as bal_in_account_currency,
sum(t1.debit) - sum(t1.credit) as bal_in_company_currency
from `tabGL Entry` t1, `tabAccount` t2
from `tabGL Entry` t1
where
t1.is_cancelled = 0
and t1.account = t2.name
and t2.report_type = 'Profit and Loss'
and t2.docstatus < 2
and t2.company = %s
and t1.account in (select name from `tabAccount` where report_type = 'Profit and Loss' and docstatus < 2 and company = %s)
and t1.posting_date between %s and %s
group by {dimension_fields}
""".format(
dimension_fields=", ".join(dimension_fields)
dimension_fields=", ".join(dimension_fields),
),
(self.company, self.get("year_start_date"), self.posting_date),
as_dict=1,

View File

@@ -4,6 +4,7 @@
import frappe
from frappe import _
from frappe.query_builder.functions import IfNull, Sum
from frappe.utils import cint, flt, get_link_to_form, getdate, nowdate
from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points
@@ -673,18 +674,22 @@ def get_bin_qty(item_code, warehouse):
def get_pos_reserved_qty(item_code, warehouse):
reserved_qty = frappe.db.sql(
"""select sum(p_item.stock_qty) as qty
from `tabPOS Invoice` p, `tabPOS Invoice Item` p_item
where p.name = p_item.parent
and ifnull(p.consolidated_invoice, '') = ''
and p_item.docstatus = 1
and p_item.item_code = %s
and p_item.warehouse = %s
""",
(item_code, warehouse),
as_dict=1,
)
p_inv = frappe.qb.DocType("POS Invoice")
p_item = frappe.qb.DocType("POS Invoice Item")
reserved_qty = (
frappe.qb.from_(p_inv)
.from_(p_item)
.select(Sum(p_item.qty).as_("qty"))
.where(
(p_inv.name == p_item.parent)
& (IfNull(p_inv.consolidated_invoice, "") == "")
& (p_inv.is_return == 0)
& (p_item.docstatus == 1)
& (p_item.item_code == item_code)
& (p_item.warehouse == warehouse)
)
).run(as_dict=True)
return reserved_qty[0].qty or 0 if reserved_qty else 0

View File

@@ -469,7 +469,7 @@
"options": "UOM"
},
{
"description": "If rate is zero them item will be treated as \"Free Item\"",
"description": "If rate is zero then item will be treated as \"Free Item\"",
"fieldname": "free_item_rate",
"fieldtype": "Currency",
"label": "Free Item Rate"
@@ -670,4 +670,4 @@
"sort_order": "DESC",
"states": [],
"title_field": "title"
}
}

View File

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

View File

@@ -1179,7 +1179,12 @@ class SalesInvoice(SellingController):
if self.is_return:
fixed_asset_gl_entries = get_gl_entries_on_asset_regain(
asset, item.base_net_amount, item.finance_book, self.get("doctype"), self.get("name")
asset,
item.base_net_amount,
item.finance_book,
self.get("doctype"),
self.get("name"),
self.get("posting_date"),
)
asset.db_set("disposal_date", None)
@@ -1194,7 +1199,12 @@ class SalesInvoice(SellingController):
asset.reload()
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(
asset, item.base_net_amount, item.finance_book, self.get("doctype"), self.get("name")
asset,
item.base_net_amount,
item.finance_book,
self.get("doctype"),
self.get("name"),
self.get("posting_date"),
)
asset.db_set("disposal_date", self.posting_date)

View File

@@ -5,7 +5,7 @@
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import cint, getdate
from frappe.utils import cint, flt, getdate
class TaxWithholdingCategory(Document):
@@ -302,7 +302,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
"docstatus": 1,
}
if not tax_details.get("consider_party_ledger_amount") and doctype != "Sales Invoice":
if doctype != "Sales Invoice":
filters.update(
{"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")}
)
@@ -569,7 +569,12 @@ def get_tds_amount_from_ldc(ldc, parties, tax_details, posting_date, net_total):
tds_amount = 0
limit_consumed = frappe.db.get_value(
"Purchase Invoice",
{"supplier": ("in", parties), "apply_tds": 1, "docstatus": 1},
{
"supplier": ("in", parties),
"apply_tds": 1,
"docstatus": 1,
"posting_date": ("between", (ldc.valid_from, ldc.valid_upto)),
},
"sum(tax_withholding_net_total)",
)
@@ -584,10 +589,10 @@ def get_tds_amount_from_ldc(ldc, parties, tax_details, posting_date, net_total):
def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details):
if current_amount < (certificate_limit - deducted_amount):
if certificate_limit - flt(deducted_amount) - flt(current_amount) >= 0:
return current_amount * rate / 100
else:
ltds_amount = certificate_limit - deducted_amount
ltds_amount = certificate_limit - flt(deducted_amount)
tds_amount = current_amount - ltds_amount
return ltds_amount * rate / 100 + tds_amount * tax_details.rate / 100
@@ -598,9 +603,9 @@ def is_valid_certificate(
):
valid = False
if (
getdate(valid_from) <= getdate(posting_date) <= getdate(valid_upto)
) and certificate_limit > deducted_amount:
available_amount = flt(certificate_limit) - flt(deducted_amount) - flt(current_amount)
if (getdate(valid_from) <= getdate(posting_date) <= getdate(valid_upto)) and available_amount > 0:
valid = True
return valid

View File

@@ -110,9 +110,9 @@ class TestTaxWithholdingCategory(unittest.TestCase):
invoices.append(pi1)
# Cumulative threshold is 30000
# Threshold calculation should be on both the invoices
# TDS should be applied only on 1000
self.assertEqual(pi1.taxes[0].tax_amount, 1000)
# Threshold calculation should be only on the Second invoice
# Second didn't breach, no TDS should be applied
self.assertEqual(pi1.taxes, [])
for d in reversed(invoices):
d.cancel()

View File

@@ -0,0 +1,41 @@
{
"creation": "2023-05-23 09:58:17.235916",
"docstatus": 0,
"doctype": "Form Tour",
"first_document": 0,
"idx": 0,
"include_name_field": 0,
"is_standard": 1,
"modified": "2023-05-23 13:10:56.227127",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
"owner": "Administrator",
"reference_doctype": "Sales Invoice",
"save_on_complete": 1,
"steps": [
{
"description": "Select a customer for whom this invoice is being prepared.",
"fieldname": "customer",
"fieldtype": "Link",
"has_next_condition": 1,
"is_table_field": 0,
"label": "Customer",
"next_step_condition": "eval: doc.customer",
"position": "Right",
"title": "Select Customer"
},
{
"child_doctype": "Sales Invoice Item",
"description": "Select item that you have sold along with quantity and rate.",
"fieldname": "items",
"fieldtype": "Table",
"has_next_condition": 0,
"is_table_field": 0,
"parent_fieldname": "items",
"position": "Top",
"title": "Select Item"
}
],
"title": "Sales Invoice"
}

View File

@@ -2,6 +2,8 @@
# License: GNU General Public License v3. See license.txt
from typing import Optional
import frappe
from frappe import _, msgprint, scrub
from frappe.contacts.doctype.address.address import (
@@ -647,12 +649,12 @@ def set_taxes(
else:
args.update(get_party_details(party, party_type))
if party_type in ("Customer", "Lead"):
if party_type in ("Customer", "Lead", "Prospect"):
args.update({"tax_type": "Sales"})
if party_type == "Lead":
if party_type in ["Lead", "Prospect"]:
args["customer"] = None
del args["lead"]
del args[frappe.scrub(party_type)]
else:
args.update({"tax_type": "Purchase"})
@@ -850,7 +852,7 @@ def get_dashboard_info(party_type, party, loyalty_program=None):
return company_wise_info
def get_party_shipping_address(doctype, name):
def get_party_shipping_address(doctype: str, name: str) -> Optional[str]:
"""
Returns an Address name (best guess) for the given doctype and name for which `address_type == 'Shipping'` is true.
and/or `is_shipping_address = 1`.
@@ -861,37 +863,41 @@ def get_party_shipping_address(doctype, name):
:param name: Party name
:return: String
"""
out = frappe.db.sql(
"SELECT dl.parent "
"from `tabDynamic Link` dl join `tabAddress` ta on dl.parent=ta.name "
"where "
"dl.link_doctype=%s "
"and dl.link_name=%s "
"and dl.parenttype='Address' "
"and ifnull(ta.disabled, 0) = 0 and"
"(ta.address_type='Shipping' or ta.is_shipping_address=1) "
"order by ta.is_shipping_address desc, ta.address_type desc limit 1",
(doctype, name),
shipping_addresses = frappe.get_all(
"Address",
filters=[
["Dynamic Link", "link_doctype", "=", doctype],
["Dynamic Link", "link_name", "=", name],
["disabled", "=", 0],
],
or_filters=[
["is_shipping_address", "=", 1],
["address_type", "=", "Shipping"],
],
pluck="name",
limit=1,
order_by="is_shipping_address DESC",
)
if out:
return out[0][0]
else:
return ""
return shipping_addresses[0] if shipping_addresses else None
def get_partywise_advanced_payment_amount(
party_type, posting_date=None, future_payment=0, company=None
party_type, posting_date=None, future_payment=0, company=None, party=None
):
cond = "1=1"
if posting_date:
if future_payment:
cond = "posting_date <= '{0}' OR DATE(creation) <= '{0}' " "".format(posting_date)
cond = "(posting_date <= '{0}' OR DATE(creation) <= '{0}')" "".format(posting_date)
else:
cond = "posting_date <= '{0}'".format(posting_date)
if company:
cond += "and company = {0}".format(frappe.db.escape(company))
if party:
cond += "and party = {0}".format(frappe.db.escape(party))
data = frappe.db.sql(
""" SELECT party, sum({0}) as amount
FROM `tabGL Entry`
@@ -903,36 +909,36 @@ def get_partywise_advanced_payment_amount(
),
party_type,
)
if data:
return frappe._dict(data)
def get_default_contact(doctype, name):
def get_default_contact(doctype: str, name: str) -> Optional[str]:
"""
Returns default contact for the given doctype and name.
Can be ordered by `contact_type` to either is_primary_contact or is_billing_contact.
Returns contact name only if there is a primary contact for given doctype and name.
Else returns None
:param doctype: Party Doctype
:param name: Party name
:return: String
"""
out = frappe.db.sql(
"""
SELECT dl.parent, c.is_primary_contact, c.is_billing_contact
FROM `tabDynamic Link` dl
INNER JOIN `tabContact` c ON c.name = dl.parent
WHERE
dl.link_doctype=%s AND
dl.link_name=%s AND
dl.parenttype = 'Contact'
ORDER BY is_primary_contact DESC, is_billing_contact DESC
""",
(doctype, name),
contacts = frappe.get_all(
"Contact",
filters=[
["Dynamic Link", "link_doctype", "=", doctype],
["Dynamic Link", "link_name", "=", name],
],
or_filters=[
["is_primary_contact", "=", 1],
["is_billing_contact", "=", 1],
],
pluck="name",
limit=1,
order_by="is_primary_contact DESC, is_billing_contact DESC",
)
if out:
try:
return out[0][0]
except Exception:
return None
else:
return None
return contacts[0] if contacts else None
def add_party_account(party_type, party, company, account):

View File

@@ -181,6 +181,16 @@ class ReceivablePayableReport(object):
return
key = (ple.against_voucher_type, ple.against_voucher_no, ple.party)
# If payment is made against credit note
# and credit note is made against a Sales Invoice
# then consider the payment against original sales invoice.
if ple.against_voucher_type in ("Sales Invoice", "Purchase Invoice"):
if ple.against_voucher_no in self.return_entries:
return_against = self.return_entries.get(ple.against_voucher_no)
if return_against:
key = (ple.against_voucher_type, return_against, ple.party)
row = self.voucher_balance.get(key)
if not row:
@@ -610,7 +620,7 @@ class ReceivablePayableReport(object):
def get_return_entries(self):
doctype = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice"
filters = {"is_return": 1, "docstatus": 1}
filters = {"is_return": 1, "docstatus": 1, "company": self.filters.company}
party_field = scrub(self.filters.party_type)
if self.filters.get(party_field):
filters.update({party_field: self.filters.get(party_field)})

View File

@@ -210,6 +210,67 @@ class TestAccountsReceivable(FrappeTestCase):
],
)
def test_payment_against_credit_note(self):
"""
Payment against credit/debit note should be considered against the parent invoice
"""
company = "_Test Company 2"
customer = "_Test Customer 2"
si1 = make_sales_invoice()
pe = get_payment_entry("Sales Invoice", si1.name, bank_account="Cash - _TC2")
pe.paid_from = "Debtors - _TC2"
pe.insert()
pe.submit()
cr_note = make_credit_note(si1.name)
si2 = make_sales_invoice()
# manually link cr_note with si2 using journal entry
je = frappe.new_doc("Journal Entry")
je.company = company
je.voucher_type = "Credit Note"
je.posting_date = today()
debit_account = "Debtors - _TC2"
debit_entry = {
"account": debit_account,
"party_type": "Customer",
"party": customer,
"debit": 100,
"debit_in_account_currency": 100,
"reference_type": cr_note.doctype,
"reference_name": cr_note.name,
"cost_center": "Main - _TC2",
}
credit_entry = {
"account": debit_account,
"party_type": "Customer",
"party": customer,
"credit": 100,
"credit_in_account_currency": 100,
"reference_type": si2.doctype,
"reference_name": si2.name,
"cost_center": "Main - _TC2",
}
je.append("accounts", debit_entry)
je.append("accounts", credit_entry)
je = je.save().submit()
filters = {
"company": company,
"report_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
}
report = execute(filters)
self.assertEqual(report[1], [])
def make_sales_invoice(no_payment_schedule=False, do_not_submit=False):
frappe.set_user("Administrator")
@@ -256,7 +317,7 @@ def make_payment(docname):
def make_credit_note(docname):
create_sales_invoice(
credit_note = create_sales_invoice(
company="_Test Company 2",
customer="_Test Customer 2",
currency="EUR",
@@ -269,3 +330,5 @@ def make_credit_note(docname):
is_return=1,
return_against=docname,
)
return credit_note

View File

@@ -31,7 +31,6 @@ class AccountsReceivableSummary(ReceivablePayableReport):
def get_data(self, args):
self.data = []
self.receivables = ReceivablePayableReport(self.filters).run(args)[1]
self.get_party_total(args)
@@ -42,6 +41,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
self.filters.report_date,
self.filters.show_future_payments,
self.filters.company,
party=self.filters.get(scrub(self.party_type)),
)
or {}
)
@@ -74,6 +74,9 @@ class AccountsReceivableSummary(ReceivablePayableReport):
row.gl_balance = gl_balance_map.get(party)
row.diff = flt(row.outstanding) - flt(row.gl_balance)
if self.filters.show_future_payments:
row.remaining_balance = flt(row.outstanding) - flt(row.future_amount)
self.data.append(row)
def get_party_total(self, args):
@@ -106,6 +109,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
"range4": 0.0,
"range5": 0.0,
"total_due": 0.0,
"future_amount": 0.0,
"sales_person": [],
}
),
@@ -151,6 +155,10 @@ class AccountsReceivableSummary(ReceivablePayableReport):
self.setup_ageing_columns()
if self.filters.show_future_payments:
self.add_column(label=_("Future Payment Amount"), fieldname="future_amount")
self.add_column(label=_("Remaining Balance"), fieldname="remaining_balance")
if self.party_type == "Customer":
self.add_column(
label=_("Territory"), fieldname="territory", fieldtype="Link", options="Territory"

View File

@@ -125,12 +125,14 @@ def get_revenue(data, period_list, include_in_gross=1):
data_to_be_removed = True
while data_to_be_removed:
revenue, data_to_be_removed = remove_parent_with_no_child(revenue, period_list)
revenue = adjust_account(revenue, period_list)
revenue, data_to_be_removed = remove_parent_with_no_child(revenue)
adjust_account_totals(revenue, period_list)
return copy.deepcopy(revenue)
def remove_parent_with_no_child(data, period_list):
def remove_parent_with_no_child(data):
data_to_be_removed = False
for parent in data:
if "is_group" in parent and parent.get("is_group") == 1:
@@ -147,16 +149,19 @@ def remove_parent_with_no_child(data, period_list):
return data, data_to_be_removed
def adjust_account(data, period_list, consolidated=False):
leaf_nodes = [item for item in data if item["is_group"] == 0]
def adjust_account_totals(data, period_list):
totals = {}
for node in leaf_nodes:
set_total(node, node["total"], data, totals)
for d in data:
for period in period_list:
key = period if consolidated else period.key
d["total"] = totals[d["account"]]
return data
for d in reversed(data):
if d.get("is_group"):
for period in period_list:
# reset totals for group accounts as totals set by get_data doesn't consider include_in_gross check
d[period.key] = sum(
item[period.key] for item in data if item.get("parent_account") == d.get("account")
)
else:
set_total(d, d["total"], data, totals)
d["total"] = totals[d["account"]]
def set_total(node, value, complete_list, totals):
@@ -191,6 +196,9 @@ def get_profit(
if profit_loss[key]:
has_value = True
if not profit_loss.get("total"):
profit_loss["total"] = 0
profit_loss["total"] += profit_loss[key]
if has_value:
return profit_loss
@@ -229,6 +237,9 @@ def get_net_profit(
if profit_loss[key]:
has_value = True
if not profit_loss.get("total"):
profit_loss["total"] = 0
profit_loss["total"] += profit_loss[key]
if has_value:
return profit_loss

View File

@@ -1,6 +1,7 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from collections import OrderedDict
import frappe
from frappe import _, qb, scrub
@@ -736,7 +737,7 @@ class GrossProfitGenerator(object):
def load_invoice_items(self):
conditions = ""
if self.filters.company:
conditions += " and company = %(company)s"
conditions += " and `tabSales Invoice`.company = %(company)s"
if self.filters.from_date:
conditions += " and posting_date >= %(from_date)s"
if self.filters.to_date:
@@ -849,30 +850,30 @@ class GrossProfitGenerator(object):
Turns list of Sales Invoice Items to a tree of Sales Invoices with their Items as children.
"""
parents = []
grouped = OrderedDict()
for row in self.si_list:
if row.parent not in parents:
parents.append(row.parent)
# initialize list with a header row for each new parent
grouped.setdefault(row.parent, [self.get_invoice_row(row)]).append(
row.update(
{"indent": 1.0, "parent_invoice": row.parent, "invoice_or_item": row.item_code}
) # descendant rows will have indent: 1.0 or greater
)
parents_index = 0
for index, row in enumerate(self.si_list):
if parents_index < len(parents) and row.parent == parents[parents_index]:
invoice = self.get_invoice_row(row)
self.si_list.insert(index, invoice)
parents_index += 1
# if item is a bundle, add it's components as seperate rows
if frappe.db.exists("Product Bundle", row.item_code):
bundled_items = self.get_bundle_items(row)
for x in bundled_items:
bundle_item = self.get_bundle_item_row(row, x)
grouped.get(row.parent).append(bundle_item)
else:
# skipping the bundle items rows
if not row.indent:
row.indent = 1.0
row.parent_invoice = row.parent
row.invoice_or_item = row.item_code
self.si_list.clear()
if frappe.db.exists("Product Bundle", row.item_code):
self.add_bundle_items(row, index)
for items in grouped.values():
self.si_list.extend(items)
def get_invoice_row(self, row):
# header row format
return frappe._dict(
{
"parent_invoice": "",
@@ -901,13 +902,6 @@ class GrossProfitGenerator(object):
}
)
def add_bundle_items(self, product_bundle, index):
bundle_items = self.get_bundle_items(product_bundle)
for i, item in enumerate(bundle_items):
bundle_item = self.get_bundle_item_row(product_bundle, item)
self.si_list.insert((index + i + 1), bundle_item)
def get_bundle_items(self, product_bundle):
return frappe.get_all(
"Product Bundle Item", filters={"parent": product_bundle.item_code}, fields=["item_code", "qty"]

View File

@@ -19,14 +19,19 @@ def execute(filters=None):
return _execute(filters)
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
def _execute(
filters=None,
additional_table_columns=None,
additional_query_columns=None,
additional_conditions=None,
):
if not filters:
filters = {}
columns = get_columns(additional_table_columns, filters)
company_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency")
item_list = get_items(filters, additional_query_columns)
item_list = get_items(filters, additional_query_columns, additional_conditions)
if item_list:
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency)
@@ -328,7 +333,7 @@ def get_columns(additional_table_columns, filters):
return columns
def get_conditions(filters):
def get_conditions(filters, additional_conditions=None):
conditions = ""
for opts in (
@@ -341,6 +346,9 @@ def get_conditions(filters):
if filters.get(opts[0]):
conditions += opts[1]
if additional_conditions:
conditions += additional_conditions
if filters.get("mode_of_payment"):
conditions += """ and exists(select name from `tabSales Invoice Payment`
where parent=`tabSales Invoice`.name
@@ -376,8 +384,8 @@ def get_group_by_conditions(filters, doctype):
return "ORDER BY `tab{0}`.{1}".format(doctype, frappe.scrub(filters.get("group_by")))
def get_items(filters, additional_query_columns):
conditions = get_conditions(filters)
def get_items(filters, additional_query_columns, additional_conditions=None):
conditions = get_conditions(filters, additional_conditions)
if additional_query_columns:
additional_query_columns = ", " + ", ".join(additional_query_columns)
@@ -391,8 +399,9 @@ def get_items(filters, additional_query_columns):
`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
`tabSales Invoice`.unrealized_profit_loss_account,
`tabSales Invoice`.is_internal_customer,
`tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
`tabSales Invoice`.customer, `tabSales Invoice`.remarks,
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
`tabSales Invoice Item`.project,
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
`tabSales Invoice Item`.`item_name`, `tabSales Invoice Item`.`item_group`,
`tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.delivery_note,

View File

@@ -5,8 +5,9 @@
"label": "Profit and Loss"
}
],
"content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Accounts\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Profit and Loss\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Chart of Accounts\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Invoice\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Receivable\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"General Ledger\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Trial Balance\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounting Masters\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"General Ledger\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Receivable\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Payable\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Financial Statements\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Multi Currency\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Bank Statement\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Subscription Management\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Share Management\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Cost Center and Budgeting\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Opening and Closing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Taxes\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Profitability\",\"col\":4}}]",
"content": "[{\"id\":\"MmUf9abwxg\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Accounts\",\"col\":12}},{\"id\":\"i0EtSjDAXq\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Profit and Loss\",\"col\":12}},{\"id\":\"X78jcbq1u3\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"vikWSkNm6_\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"id\":\"pMywM0nhlj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Chart of Accounts\",\"col\":3}},{\"id\":\"_pRdD6kqUG\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"id\":\"G984SgVRJN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Invoice\",\"col\":3}},{\"id\":\"1ArNvt9qhz\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"id\":\"F9f4I1viNr\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"id\":\"4IBBOIxfqW\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Receivable\",\"col\":3}},{\"id\":\"El2anpPaFY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"General Ledger\",\"col\":3}},{\"id\":\"1nwcM9upJo\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Trial Balance\",\"col\":3}},{\"id\":\"OF9WOi1Ppc\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"B7-uxs8tkU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"tHb3yxthkR\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"id\":\"DnNtsmxpty\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounting Masters\",\"col\":4}},{\"id\":\"nKKr6fjgjb\",\"type\":\"card\",\"data\":{\"card_name\":\"General Ledger\",\"col\":4}},{\"id\":\"xOHTyD8b5l\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Receivable\",\"col\":4}},{\"id\":\"_Cb7C8XdJJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Payable\",\"col\":4}},{\"id\":\"p7NY6MHe2Y\",\"type\":\"card\",\"data\":{\"card_name\":\"Financial Statements\",\"col\":4}},{\"id\":\"KlqilF5R_V\",\"type\":\"card\",\"data\":{\"card_name\":\"Taxes\",\"col\":4}},{\"id\":\"jTUy8LB0uw\",\"type\":\"card\",\"data\":{\"card_name\":\"Cost Center and Budgeting\",\"col\":4}},{\"id\":\"Wn2lhs7WLn\",\"type\":\"card\",\"data\":{\"card_name\":\"Multi Currency\",\"col\":4}},{\"id\":\"PAQMqqNkBM\",\"type\":\"card\",\"data\":{\"card_name\":\"Bank Statement\",\"col\":4}},{\"id\":\"Q_hBCnSeJY\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"3AK1Zf0oew\",\"type\":\"card\",\"data\":{\"card_name\":\"Profitability\",\"col\":4}},{\"id\":\"kxhoaiqdLq\",\"type\":\"card\",\"data\":{\"card_name\":\"Opening and Closing\",\"col\":4}},{\"id\":\"q0MAlU2j_Z\",\"type\":\"card\",\"data\":{\"card_name\":\"Subscription Management\",\"col\":4}},{\"id\":\"ptm7T6Hwu-\",\"type\":\"card\",\"data\":{\"card_name\":\"Share Management\",\"col\":4}},{\"id\":\"OX7lZHbiTr\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
"creation": "2020-03-02 15:41:59.515192",
"custom_blocks": [],
"docstatus": 0,
"doctype": "Workspace",
"for_user": "",
@@ -1093,10 +1094,11 @@
"type": "Link"
}
],
"modified": "2022-06-24 05:41:09.236458",
"modified": "2023-05-30 13:23:29.316711",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting",
"number_cards": [],
"owner": "Administrator",
"parent_page": "",
"public": 1,

View File

@@ -26,6 +26,7 @@ from erpnext.accounts.general_ledger import make_reverse_gl_entries
from erpnext.assets.doctype.asset.depreciation import (
get_depreciation_accounts,
get_disposal_account_and_cost_center,
is_first_day_of_the_month,
is_last_day_of_the_month,
)
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
@@ -337,13 +338,9 @@ class Asset(AccountsController):
if should_get_last_day:
schedule_date = get_last_day(schedule_date)
# schedule date will be a year later from start date
# so monthly schedule date is calculated by removing 11 months from it
monthly_schedule_date = add_months(schedule_date, -finance_book.frequency_of_depreciation + 1)
# if asset is being sold
if date_of_disposal:
from_date = self.get_from_date(finance_book.finance_book)
from_date = self.get_from_date_for_disposal(finance_book)
depreciation_amount, days, months = self.get_pro_rata_amt(
finance_book,
depreciation_amount,
@@ -364,9 +361,9 @@ class Asset(AccountsController):
# For first row
if (
(has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata)
n == 0
and (has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata)
and not self.opening_accumulated_depreciation
and n == 0
):
from_date = add_days(
self.available_for_use_date, -1
@@ -378,10 +375,26 @@ class Asset(AccountsController):
finance_book.depreciation_start_date,
has_wdv_or_dd_non_yearly_pro_rata,
)
# For first depr schedule date will be the start date
# so monthly schedule date is calculated by removing month difference between use date and start date
monthly_schedule_date = add_months(finance_book.depreciation_start_date, -months + 1)
elif n == 0 and has_wdv_or_dd_non_yearly_pro_rata and self.opening_accumulated_depreciation:
if not is_first_day_of_the_month(getdate(self.available_for_use_date)):
from_date = get_last_day(
add_months(
getdate(self.available_for_use_date),
((self.number_of_depreciations_booked - 1) * finance_book.frequency_of_depreciation),
)
)
else:
from_date = add_months(
getdate(add_days(self.available_for_use_date, -1)),
(self.number_of_depreciations_booked * finance_book.frequency_of_depreciation),
)
depreciation_amount, days, months = self.get_pro_rata_amt(
finance_book,
depreciation_amount,
from_date,
finance_book.depreciation_start_date,
has_wdv_or_dd_non_yearly_pro_rata,
)
# For last row
elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1:
@@ -406,9 +419,7 @@ class Asset(AccountsController):
depreciation_amount_without_pro_rata, depreciation_amount, finance_book.finance_book
)
monthly_schedule_date = add_months(schedule_date, 1)
schedule_date = add_days(schedule_date, days)
last_schedule_date = schedule_date
if not depreciation_amount:
continue
@@ -425,7 +436,7 @@ class Asset(AccountsController):
depreciation_amount += value_after_depreciation - finance_book.expected_value_after_useful_life
skip_row = True
if depreciation_amount > 0:
if flt(depreciation_amount, self.precision("gross_purchase_amount")) > 0:
self._add_depreciation_row(
schedule_date,
depreciation_amount,
@@ -500,16 +511,19 @@ class Asset(AccountsController):
return start
def get_from_date(self, finance_book):
def get_from_date_for_disposal(self, finance_book):
if not self.get("schedules"):
return self.available_for_use_date
return add_months(
getdate(self.available_for_use_date),
(self.number_of_depreciations_booked * finance_book.frequency_of_depreciation),
)
if len(self.finance_books) == 1:
return self.schedules[-1].schedule_date
from_date = ""
for schedule in self.get("schedules"):
if schedule.finance_book == finance_book:
if schedule.finance_book == finance_book.finance_book:
from_date = schedule.schedule_date
if from_date:
@@ -1287,9 +1301,11 @@ def get_straight_line_or_manual_depr_amount(asset, row):
)
# if the Depreciation Schedule is being prepared for the first time
else:
return (flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)) / flt(
row.total_number_of_depreciations
)
return (
flt(asset.gross_purchase_amount)
- flt(asset.opening_accumulated_depreciation)
- flt(row.expected_value_after_useful_life)
) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked)
def get_wdv_or_dd_depr_amount(

View File

@@ -4,7 +4,16 @@
import frappe
from frappe import _
from frappe.utils import add_months, cint, flt, get_last_day, getdate, nowdate, today
from frappe.utils import (
add_months,
cint,
flt,
get_first_day,
get_last_day,
getdate,
nowdate,
today,
)
from frappe.utils.data import get_link_to_form
from frappe.utils.user import get_users_with_role
@@ -272,7 +281,7 @@ def scrap_asset(asset_name):
je.company = asset.company
je.remark = "Scrap Entry for asset {0}".format(asset_name)
for entry in get_gl_entries_on_asset_disposal(asset):
for entry in get_gl_entries_on_asset_disposal(asset, date):
entry.update({"reference_type": "Asset", "reference_name": asset_name})
je.append("accounts", entry)
@@ -395,8 +404,11 @@ def disposal_happens_in_the_future(posting_date_of_disposal):
def get_gl_entries_on_asset_regain(
asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None
asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None, date=None
):
if not date:
date = getdate()
(
fixed_asset_account,
asset,
@@ -414,7 +426,7 @@ def get_gl_entries_on_asset_regain(
"debit_in_account_currency": asset.gross_purchase_amount,
"debit": asset.gross_purchase_amount,
"cost_center": depreciation_cost_center,
"posting_date": getdate(),
"posting_date": date,
},
item=asset,
),
@@ -424,7 +436,7 @@ def get_gl_entries_on_asset_regain(
"credit_in_account_currency": accumulated_depr_amount,
"credit": accumulated_depr_amount,
"cost_center": depreciation_cost_center,
"posting_date": getdate(),
"posting_date": date,
},
item=asset,
),
@@ -433,7 +445,7 @@ def get_gl_entries_on_asset_regain(
profit_amount = abs(flt(value_after_depreciation)) - abs(flt(selling_amount))
if profit_amount:
get_profit_gl_entries(
asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center
asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center, date
)
if voucher_type and voucher_no:
@@ -445,8 +457,11 @@ def get_gl_entries_on_asset_regain(
def get_gl_entries_on_asset_disposal(
asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None
asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None, date=None
):
if not date:
date = getdate()
(
fixed_asset_account,
asset,
@@ -464,7 +479,7 @@ def get_gl_entries_on_asset_disposal(
"credit_in_account_currency": asset.gross_purchase_amount,
"credit": asset.gross_purchase_amount,
"cost_center": depreciation_cost_center,
"posting_date": getdate(),
"posting_date": date,
},
item=asset,
),
@@ -474,7 +489,7 @@ def get_gl_entries_on_asset_disposal(
"debit_in_account_currency": accumulated_depr_amount,
"debit": accumulated_depr_amount,
"cost_center": depreciation_cost_center,
"posting_date": getdate(),
"posting_date": date,
},
item=asset,
),
@@ -483,7 +498,7 @@ def get_gl_entries_on_asset_disposal(
profit_amount = flt(selling_amount) - flt(value_after_depreciation)
if profit_amount:
get_profit_gl_entries(
asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center
asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center, date
)
if voucher_type and voucher_no:
@@ -517,8 +532,12 @@ def get_asset_details(asset, finance_book=None):
def get_profit_gl_entries(
asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center
asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center, date=None
):
if not date:
date = getdate()
debit_or_credit = "debit" if profit_amount < 0 else "credit"
gl_entries.append(
asset.get_gl_dict(
@@ -527,7 +546,7 @@ def get_profit_gl_entries(
"cost_center": depreciation_cost_center,
debit_or_credit: abs(profit_amount),
debit_or_credit + "_in_account_currency": abs(profit_amount),
"posting_date": getdate(),
"posting_date": date,
},
item=asset,
)
@@ -581,3 +600,9 @@ def is_last_day_of_the_month(date):
last_day_of_the_month = get_last_day(date)
return getdate(last_day_of_the_month) == getdate(date)
def is_first_day_of_the_month(date):
first_day_of_the_month = get_first_day(date)
return getdate(first_day_of_the_month) == getdate(date)

View File

@@ -327,6 +327,79 @@ class TestAsset(AssetSetup):
si.cancel()
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated")
def test_gle_made_by_asset_sale_for_existing_asset(self):
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
asset = create_asset(
calculate_depreciation=1,
available_for_use_date="2020-04-01",
purchase_date="2020-04-01",
expected_value_after_useful_life=0,
total_number_of_depreciations=5,
number_of_depreciations_booked=2,
frequency_of_depreciation=12,
depreciation_start_date="2023-03-31",
opening_accumulated_depreciation=24000,
gross_purchase_amount=60000,
submit=1,
)
expected_depr_values = [
["2023-03-31", 12000, 36000],
["2024-03-31", 12000, 48000],
["2025-03-31", 12000, 60000],
]
for i, schedule in enumerate(asset.schedules):
self.assertEqual(getdate(expected_depr_values[i][0]), schedule.schedule_date)
self.assertEqual(expected_depr_values[i][1], schedule.depreciation_amount)
self.assertEqual(expected_depr_values[i][2], schedule.accumulated_depreciation_amount)
post_depreciation_entries(date="2023-03-31")
si = create_sales_invoice(
item_code="Macbook Pro", asset=asset.name, qty=1, rate=40000, posting_date=getdate("2023-05-23")
)
asset.load_from_db()
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
expected_values = [["2023-03-31", 12000, 36000], ["2023-05-23", 1742.47, 37742.47]]
for i, schedule in enumerate(asset.schedules):
self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
self.assertTrue(schedule.journal_entry)
expected_gle = (
(
"_Test Accumulated Depreciations - _TC",
37742.47,
0.0,
),
(
"_Test Fixed Asset - _TC",
0.0,
60000.0,
),
(
"_Test Gain/Loss on Asset Disposal - _TC",
0.0,
17742.47,
),
("Debtors - _TC", 40000.0, 0.0),
)
gle = frappe.db.sql(
"""select account, debit, credit from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no = %s
order by account""",
si.name,
)
self.assertSequenceEqual(gle, expected_gle)
def test_asset_with_maintenance_required_status_after_sale(self):
asset = create_asset(
calculate_depreciation=1,
@@ -649,7 +722,7 @@ class TestDepreciationMethods(AssetSetup):
)
self.assertEqual(asset.status, "Draft")
expected_schedules = [["2032-12-31", 30000.0, 77095.89], ["2033-06-06", 12904.11, 90000.0]]
expected_schedules = [["2032-12-31", 42904.11, 90000.0]]
schedules = [
[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
for d in asset.get("schedules")
@@ -693,14 +766,14 @@ class TestDepreciationMethods(AssetSetup):
number_of_depreciations_booked=1,
opening_accumulated_depreciation=50000,
expected_value_after_useful_life=10000,
depreciation_start_date="2030-12-31",
depreciation_start_date="2031-12-31",
total_number_of_depreciations=3,
frequency_of_depreciation=12,
)
self.assertEqual(asset.status, "Draft")
expected_schedules = [["2030-12-31", 33333.50, 83333.50], ["2031-12-31", 6666.50, 90000.0]]
expected_schedules = [["2031-12-31", 33333.50, 83333.50], ["2032-12-31", 6666.50, 90000.0]]
schedules = [
[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]

View File

@@ -436,6 +436,7 @@ class AssetCapitalization(StockController):
item.get("finance_book") or self.get("finance_book"),
self.get("doctype"),
self.get("name"),
self.get("posting_date"),
)
asset.db_set("disposal_date", self.posting_date)

View File

@@ -96,7 +96,6 @@ class AssetCategory(Document):
frappe.throw(msg, title=_("Missing Account"))
@frappe.whitelist()
def get_asset_category_account(
fieldname, item=None, asset=None, account=None, asset_category=None, company=None
):

View File

@@ -7,12 +7,14 @@
],
"content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Assets\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Asset Value Analytics\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Asset\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Asset Category\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Fixed Asset Register\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Assets\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Maintenance\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]",
"creation": "2020-03-02 15:43:27.634865",
"custom_blocks": [],
"docstatus": 0,
"doctype": "Workspace",
"for_user": "",
"hide_custom": 0,
"icon": "assets",
"idx": 0,
"is_hidden": 0,
"label": "Assets",
"links": [
{
@@ -183,13 +185,15 @@
"type": "Link"
}
],
"modified": "2021-10-04 12:15:54.839454",
"modified": "2023-05-24 14:47:20.243146",
"modified_by": "Administrator",
"module": "Assets",
"name": "Assets",
"number_cards": [],
"owner": "Administrator",
"parent_page": "",
"parent_page": "Accounting",
"public": 1,
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
"sequence_id": 4.0,
@@ -216,4 +220,4 @@
}
],
"title": "Assets"
}
}

View File

@@ -157,7 +157,7 @@
"party_account_currency",
"inter_company_order_reference",
"is_old_subcontracting_flow",
"dashboard"
"connections_tab"
],
"fields": [
{
@@ -1171,7 +1171,6 @@
"depends_on": "is_internal_supplier",
"fieldname": "set_from_warehouse",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Set From Warehouse",
"options": "Warehouse"
},
@@ -1185,12 +1184,6 @@
"fieldtype": "Tab Break",
"label": "More Info"
},
{
"fieldname": "dashboard",
"fieldtype": "Tab Break",
"label": "Dashboard",
"show_dashboard": 1
},
{
"fieldname": "column_break_7",
"fieldtype": "Column Break"
@@ -1266,13 +1259,19 @@
"fieldname": "shipping_address_section",
"fieldtype": "Section Break",
"label": "Shipping Address"
},
{
"fieldname": "connections_tab",
"fieldtype": "Tab Break",
"label": "Connections",
"show_dashboard": 1
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"links": [],
"modified": "2023-05-07 20:18:09.196799",
"modified": "2023-05-24 11:16:41.195340",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",

View File

@@ -7,12 +7,14 @@
],
"content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Buying\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Purchase Order Trends\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Material Request\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Analytics\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Order Analysis\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Buying\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Items & Pricing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Supplier\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Supplier Scorecard\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Regional\",\"col\":4}}]",
"creation": "2020-01-28 11:50:26.195467",
"custom_blocks": [],
"docstatus": 0,
"doctype": "Workspace",
"for_user": "",
"hide_custom": 0,
"icon": "buying",
"idx": 0,
"is_hidden": 0,
"label": "Buying",
"links": [
{
@@ -509,16 +511,18 @@
"type": "Link"
}
],
"modified": "2022-01-13 17:26:39.090190",
"modified": "2023-05-24 14:47:20.535772",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying",
"number_cards": [],
"owner": "Administrator",
"parent_page": "",
"public": 1,
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
"sequence_id": 6.0,
"sequence_id": 5.0,
"shortcuts": [
{
"color": "Green",

View File

@@ -392,6 +392,9 @@ class AccountsController(TransactionBase):
)
def validate_inter_company_reference(self):
if self.get("is_return"):
return
if self.doctype not in ("Purchase Invoice", "Purchase Receipt"):
return
@@ -755,6 +758,7 @@ class AccountsController(TransactionBase):
}
)
update_gl_dict_with_regional_fields(self, gl_dict)
accounting_dimensions = get_accounting_dimensions()
dimension_dict = frappe._dict()
@@ -913,6 +917,9 @@ class AccountsController(TransactionBase):
return is_inclusive
def should_show_taxes_as_table_in_print(self):
return cint(frappe.db.get_single_value("Accounts Settings", "show_taxes_as_table_in_print"))
def validate_advance_entries(self):
order_field = "sales_order" if self.doctype == "Sales Invoice" else "purchase_order"
order_list = list(set(d.get(order_field) for d in self.get("items") if d.get(order_field)))
@@ -1679,6 +1686,9 @@ class AccountsController(TransactionBase):
d.base_payment_amount = flt(
d.payment_amount * self.get("conversion_rate"), d.precision("base_payment_amount")
)
else:
self.fetch_payment_terms_from_order(po_or_so, doctype)
self.ignore_default_payment_terms_template = 1
def get_order_details(self):
if self.doctype == "Sales Invoice":
@@ -2829,3 +2839,8 @@ def validate_regional(doc):
@erpnext.allow_regional
def validate_einvoice_fields(doc):
pass
@erpnext.allow_regional
def update_gl_dict_with_regional_fields(doc, gl_dict):
pass

View File

@@ -184,6 +184,7 @@ class BuyingController(SubcontractingController):
address_dict = {
"supplier_address": "address_display",
"shipping_address": "shipping_address_display",
"billing_address": "billing_address_display",
}
for address_field, address_display_field in address_dict.items():

View File

@@ -30,10 +30,16 @@ def set_print_templates_for_taxes(doc, settings):
doc.print_templates.update(
{
"total": "templates/print_formats/includes/total.html",
"taxes": "templates/print_formats/includes/taxes.html",
}
)
if not doc.should_show_taxes_as_table_in_print():
doc.print_templates.update(
{
"taxes": "templates/print_formats/includes/taxes.html",
}
)
def format_columns(display_columns, compact_fields):
compact_fields = compact_fields + ["image", "item_code", "item_name"]

View File

@@ -43,7 +43,6 @@ class SellingController(StockController):
self.validate_auto_repeat_subscription_dates()
def set_missing_values(self, for_validate=False):
super(SellingController, self).set_missing_values(for_validate)
# set contact and address details for customer, if they are not mentioned
@@ -62,7 +61,7 @@ class SellingController(StockController):
elif self.doctype == "Quotation" and self.party_name:
if self.quotation_to == "Customer":
customer = self.party_name
else:
elif self.quotation_to == "Lead":
lead = self.party_name
if customer:
@@ -171,7 +170,7 @@ class SellingController(StockController):
self.round_floats_in(sales_person)
sales_person.allocated_amount = flt(
self.amount_eligible_for_commission * sales_person.allocated_percentage / 100.0,
flt(self.amount_eligible_for_commission) * sales_person.allocated_percentage / 100.0,
self.precision("allocated_amount", sales_person),
)

View File

@@ -442,7 +442,43 @@ class StockController(AccountsController):
if not dimension:
continue
if row.get(dimension.source_fieldname):
if self.doctype in [
"Purchase Invoice",
"Purchase Receipt",
"Sales Invoice",
"Delivery Note",
"Stock Entry",
]:
if (
(
sl_dict.actual_qty > 0
and not self.get("is_return")
or sl_dict.actual_qty < 0
and self.get("is_return")
)
and self.doctype in ["Purchase Invoice", "Purchase Receipt"]
) or (
(
sl_dict.actual_qty < 0
and not self.get("is_return")
or sl_dict.actual_qty > 0
and self.get("is_return")
)
and self.doctype in ["Sales Invoice", "Delivery Note", "Stock Entry"]
):
sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname)
else:
fieldname_start_with = "to"
if self.doctype in ["Purchase Invoice", "Purchase Receipt"]:
fieldname_start_with = "from"
fieldname = f"{fieldname_start_with}_{dimension.source_fieldname}"
sl_dict[dimension.target_fieldname] = row.get(fieldname)
if not sl_dict.get(dimension.target_fieldname):
sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname)
elif 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:
@@ -734,6 +770,9 @@ class StockController(AccountsController):
}
)
if self.docstatus == 2:
force = True
if force or future_sle_exists(args) or repost_required_for_queue(self):
item_based_reposting = cint(
frappe.db.get_single_value("Stock Reposting Settings", "item_based_reposting")

View File

@@ -689,7 +689,6 @@ class SubcontractingController(StockController):
"actual_qty": flt(item.rejected_qty) * flt(item.conversion_factor),
"serial_no": cstr(item.rejected_serial_no).strip(),
"incoming_rate": 0.0,
"recalculate_rate": 1,
},
)
)
@@ -741,7 +740,7 @@ class SubcontractingController(StockController):
sco_doc = frappe.get_doc("Subcontracting Order", sco)
sco_doc.update_status()
def set_missing_values_in_additional_costs(self):
def calculate_additional_costs(self):
self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs"))
if self.total_additional_costs:

View File

@@ -36,7 +36,7 @@ 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):
def test_calculate_additional_costs(self):
sco = get_subcontracting_order(do_not_submit=1)
rate_without_additional_cost = sco.items[0].rate

View File

@@ -3,7 +3,10 @@
import frappe
from frappe import _
from frappe.contacts.address_and_contact import load_address_and_contact
from frappe.contacts.address_and_contact import (
delete_contact_and_address,
load_address_and_contact,
)
from frappe.email.inbox import link_communication_to_document
from frappe.model.mapper import get_mapped_doc
from frappe.utils import comma_and, get_link_to_form, has_gravatar, validate_email_address
@@ -43,9 +46,8 @@ class Lead(SellingController, CRMNote):
self.update_prospect()
def on_trash(self):
frappe.db.sql("""update `tabIssue` set lead='' where lead=%s""", self.name)
self.unlink_dynamic_links()
frappe.db.set_value("Issue", {"lead": self.name}, "lead", None)
delete_contact_and_address(self.doctype, self.name)
self.remove_link_from_prospect()
def set_full_name(self):
@@ -122,27 +124,6 @@ class Lead(SellingController, CRMNote):
)
lead_row.db_update()
def unlink_dynamic_links(self):
links = frappe.get_all(
"Dynamic Link",
filters={"link_doctype": self.doctype, "link_name": self.name},
fields=["parent", "parenttype"],
)
for link in links:
linked_doc = frappe.get_doc(link["parenttype"], link["parent"])
if len(linked_doc.get("links")) == 1:
linked_doc.delete(ignore_permissions=True)
else:
to_remove = None
for d in linked_doc.get("links"):
if d.link_doctype == self.doctype and d.link_name == self.name:
to_remove = d
if to_remove:
linked_doc.remove(to_remove)
linked_doc.save(ignore_permissions=True)
def remove_link_from_prospect(self):
prospects = self.get_linked_prospects()

View File

@@ -5,7 +5,7 @@ frappe.ui.form.on('LinkedIn Settings', {
onload: function(frm) {
if (frm.doc.session_status == 'Expired' && frm.doc.consumer_key && frm.doc.consumer_secret) {
frappe.confirm(
__('Session not valid, Do you want to login?'),
__('Session not valid. Do you want to login?'),
function(){
frm.trigger("login");
},
@@ -14,11 +14,11 @@ frappe.ui.form.on('LinkedIn Settings', {
}
);
}
frm.dashboard.set_headline(__("For more information, {0}.", [`<a target='_blank' href='https://docs.erpnext.com/docs/user/manual/en/CRM/linkedin-settings'>${__('Click here')}</a>`]));
frm.dashboard.set_headline(__("For more information, {0}.", [`<a target='_blank' href='https://docs.erpnext.com/docs/user/manual/en/CRM/linkedin-settings'>${__('click here')}</a>`]));
},
refresh: function(frm) {
if (frm.doc.session_status=="Expired"){
let msg = __("Session Not Active. Save doc to login.");
let msg = __("Session not active. Save document to login.");
frm.dashboard.set_headline_alert(
`<div class="row">
<div class="col-xs-12">
@@ -37,7 +37,7 @@ frappe.ui.form.on('LinkedIn Settings', {
let msg,color;
if (days>0){
msg = __("Your Session will be expire in {0} days.", [days]);
msg = __("Your session will be expire in {0} days.", [days]);
color = "green";
}
else {

View File

@@ -2,7 +2,10 @@
# For license information, please see license.txt
import frappe
from frappe.contacts.address_and_contact import load_address_and_contact
from frappe.contacts.address_and_contact import (
delete_contact_and_address,
load_address_and_contact,
)
from frappe.model.mapper import get_mapped_doc
from erpnext.crm.utils import CRMNote, copy_comments, link_communications, link_open_events
@@ -16,7 +19,7 @@ class Prospect(CRMNote):
self.link_with_lead_contact_and_address()
def on_trash(self):
self.unlink_dynamic_links()
delete_contact_and_address(self.doctype, self.name)
def after_insert(self):
carry_forward_communication_and_comments = frappe.db.get_single_value(
@@ -54,27 +57,6 @@ class Prospect(CRMNote):
linked_doc.append("links", {"link_doctype": self.doctype, "link_name": self.name})
linked_doc.save(ignore_permissions=True)
def unlink_dynamic_links(self):
links = frappe.get_all(
"Dynamic Link",
filters={"link_doctype": self.doctype, "link_name": self.name},
fields=["parent", "parenttype"],
)
for link in links:
linked_doc = frappe.get_doc(link["parenttype"], link["parent"])
if len(linked_doc.get("links")) == 1:
linked_doc.delete(ignore_permissions=True)
else:
to_remove = None
for d in linked_doc.get("links"):
if d.link_doctype == self.doctype and d.link_name == self.name:
to_remove = d
if to_remove:
linked_doc.remove(to_remove)
linked_doc.save(ignore_permissions=True)
@frappe.whitelist()
def make_customer(source_name, target_doc=None):

View File

@@ -14,7 +14,7 @@ frappe.ui.form.on('Twitter Settings', {
}
);
}
frm.dashboard.set_headline(__("For more information, {0}.", [`<a target='_blank' href='https://docs.erpnext.com/docs/user/manual/en/CRM/twitter-settings'>${__('Click here')}</a>`]));
frm.dashboard.set_headline(__("For more information, {0}.", [`<a target='_blank' href='https://docs.erpnext.com/docs/user/manual/en/CRM/twitter-settings'>${__('click here')}</a>`]));
},
refresh: function(frm) {
let msg, color, flag=false;

View File

@@ -5,177 +5,18 @@
"label": "Territory Wise Sales"
}
],
"content": "[{\"type\":\"chart\",\"data\":{\"chart_name\":\"Territory Wise Sales\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Lead\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Opportunity\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customer\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Sales Pipeline\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Campaign\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Maintenance\",\"col\":4}}]",
"content": "[{\"id\":\"Cj2TyhgiWy\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Territory Wise Sales\",\"col\":12}},{\"id\":\"LAKRmpYMRA\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"XGIwEUStw_\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"id\":\"69RN0XsiJK\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Lead\",\"col\":3}},{\"id\":\"t6PQ0vY-Iw\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Opportunity\",\"col\":3}},{\"id\":\"VOFE0hqXRD\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Customer\",\"col\":3}},{\"id\":\"0ik53fuemG\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"id\":\"wdROEmB_XG\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"-I9HhcgUKE\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"ttpROKW9vk\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"id\":\"-76QPdbBHy\",\"type\":\"card\",\"data\":{\"card_name\":\"Sales Pipeline\",\"col\":4}},{\"id\":\"_YmGwzVWRr\",\"type\":\"card\",\"data\":{\"card_name\":\"Masters\",\"col\":4}},{\"id\":\"Bma1PxoXk3\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"80viA0R83a\",\"type\":\"card\",\"data\":{\"card_name\":\"Campaign\",\"col\":4}},{\"id\":\"Buo5HtKRFN\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"sLS_x4FMK2\",\"type\":\"card\",\"data\":{\"card_name\":\"Maintenance\",\"col\":4}}]",
"creation": "2020-01-23 14:48:30.183272",
"custom_blocks": [],
"docstatus": 0,
"doctype": "Workspace",
"for_user": "",
"hide_custom": 0,
"icon": "crm",
"idx": 0,
"is_hidden": 0,
"label": "CRM",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Sales Pipeline",
"link_count": 0,
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Lead",
"link_count": 0,
"link_to": "Lead",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Opportunity",
"link_count": 0,
"link_to": "Opportunity",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Prospect",
"link_count": 0,
"link_to": "Prospect",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Customer",
"link_count": 0,
"link_to": "Customer",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Contact",
"link_count": 0,
"link_to": "Contact",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Communication",
"link_count": 0,
"link_to": "Communication",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Contract",
"link_count": 0,
"link_to": "Contract",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Appointment",
"link_count": 0,
"link_to": "Appointment",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Newsletter",
"link_count": 0,
"link_to": "Newsletter",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Lead Source",
"link_count": 0,
"link_to": "Lead Source",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Territory",
"link_count": 0,
"link_to": "Territory",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Customer Group",
"link_count": 0,
"link_to": "Customer Group",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Sales Person",
"link_count": 0,
"link_to": "Sales Person",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Sales Stage",
"link_count": 0,
"link_to": "Sales Stage",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
@@ -446,19 +287,183 @@
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Masters",
"link_count": 7,
"onboard": 0,
"type": "Card Break"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Territory",
"link_count": 0,
"link_to": "Territory",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Customer Group",
"link_count": 0,
"link_to": "Customer Group",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Contact",
"link_count": 0,
"link_to": "Contact",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Prospect",
"link_count": 0,
"link_to": "Prospect",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Sales Person",
"link_count": 0,
"link_to": "Sales Person",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Sales Stage",
"link_count": 0,
"link_to": "Sales Stage",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Lead Source",
"link_count": 0,
"link_to": "Lead Source",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Sales Pipeline",
"link_count": 7,
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Lead",
"link_count": 0,
"link_to": "Lead",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Opportunity",
"link_count": 0,
"link_to": "Opportunity",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Customer",
"link_count": 0,
"link_to": "Customer",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Contract",
"link_count": 0,
"link_to": "Contract",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Appointment",
"link_count": 0,
"link_to": "Appointment",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Newsletter",
"link_count": 0,
"link_to": "Newsletter",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Communication",
"link_count": 0,
"link_to": "Communication",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
"modified": "2022-07-22 15:03:30.755417",
"modified": "2023-05-26 16:49:04.298122",
"modified_by": "Administrator",
"module": "CRM",
"name": "CRM",
"number_cards": [],
"owner": "Administrator",
"parent_page": "",
"public": 1,
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
"sequence_id": 7.0,
"sequence_id": 10.0,
"shortcuts": [
{
"color": "Blue",

View File

@@ -78,9 +78,10 @@ erpnext.ProductList = class {
let title_html = `<div style="display: flex; margin-left: -15px;">`;
title_html += `
<div class="col-8" style="margin-right: -15px;">
<a class="" href="/${ item.route || '#' }"
style="color: var(--gray-800); font-weight: 500;">
<a href="/${ item.route || '#' }">
<div class="product-title">
${ title }
</div>
</a>
</div>
`;
@@ -201,4 +202,4 @@ erpnext.ProductList = class {
}
}
};
};

View File

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

View File

@@ -1,30 +1,185 @@
{
"charts": [],
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Marketplace\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Payments\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
"content": "[{\"id\":\"e88ADOJ7WC\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Integrations</b></span>\",\"col\":12}},{\"id\":\"G0tyx9WOfm\",\"type\":\"card\",\"data\":{\"card_name\":\"Backup\",\"col\":4}},{\"id\":\"nu4oSjH5Rd\",\"type\":\"card\",\"data\":{\"card_name\":\"Authentication\",\"col\":4}},{\"id\":\"nG8cdkpzoc\",\"type\":\"card\",\"data\":{\"card_name\":\"Google Services\",\"col\":4}},{\"id\":\"4hwuQn6E95\",\"type\":\"card\",\"data\":{\"card_name\":\"Communication Channels\",\"col\":4}},{\"id\":\"sEGAzTJRmq\",\"type\":\"card\",\"data\":{\"card_name\":\"Payments\",\"col\":4}},{\"id\":\"ZC6xu-cLBR\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
"creation": "2020-08-20 19:30:48.138801",
"custom_blocks": [],
"docstatus": 0,
"doctype": "Workspace",
"for_user": "",
"hide_custom": 0,
"icon": "integration",
"idx": 0,
"is_hidden": 0,
"label": "ERPNext Integrations",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Marketplace",
"link_count": 0,
"label": "Backup",
"link_count": 3,
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Woocommerce Settings",
"label": "Dropbox Settings",
"link_count": 0,
"link_to": "Woocommerce Settings",
"link_to": "Dropbox Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "S3 Backup Settings",
"link_count": 0,
"link_to": "S3 Backup Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Google Drive",
"link_count": 0,
"link_to": "Google Drive",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Authentication",
"link_count": 4,
"onboard": 0,
"type": "Card Break"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Social Login",
"link_count": 0,
"link_to": "Social Login Key",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "LDAP Settings",
"link_count": 0,
"link_to": "LDAP Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "OAuth Client",
"link_count": 0,
"link_to": "OAuth Client",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "OAuth Provider Settings",
"link_count": 0,
"link_to": "OAuth Provider Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Communication Channels",
"link_count": 3,
"onboard": 0,
"type": "Card Break"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Webhook",
"link_count": 0,
"link_to": "Webhook",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "SMS Settings",
"link_count": 0,
"link_to": "SMS Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Slack Webhook URL",
"link_count": 0,
"link_to": "Slack Webhook URL",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Google Services",
"link_count": 4,
"onboard": 0,
"type": "Card Break"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Google Settings",
"link_count": 0,
"link_to": "Google Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Google Contacts",
"link_count": 0,
"link_to": "Google Contacts",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Google Calendar",
"link_count": 0,
"link_to": "Google Calendar",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Google Drive",
"link_count": 0,
"link_to": "Google Drive",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -33,12 +188,11 @@
"hidden": 0,
"is_query_report": 0,
"label": "Payments",
"link_count": 0,
"link_count": 3,
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "GoCardless Settings",
@@ -49,10 +203,9 @@
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "M-Pesa Settings",
"label": "Mpesa Settings",
"link_count": 0,
"link_to": "Mpesa Settings",
"link_type": "DocType",
@@ -60,15 +213,6 @@
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Settings",
"link_count": 0,
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Plaid Settings",
@@ -78,6 +222,14 @@
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Settings",
"link_count": 2,
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
@@ -88,18 +240,30 @@
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Woocommerce Settings",
"link_count": 0,
"link_to": "Woocommerce Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
"modified": "2022-01-13 17:35:35.508718",
"modified": "2023-05-24 14:47:25.984717",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "ERPNext Integrations",
"number_cards": [],
"owner": "Administrator",
"parent_page": "",
"public": 1,
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
"sequence_id": 10.0,
"sequence_id": 21.0,
"shortcuts": [],
"title": "ERPNext Integrations"
}
}

View File

@@ -160,4 +160,3 @@ class TestLoanDisbursement(unittest.TestCase):
interest = per_day_interest * 15
self.assertEqual(amounts["pending_principal_amount"], 1500000)
self.assertEqual(amounts["interest_amount"], flt(interest + previous_interest, 2))

View File

@@ -22,7 +22,7 @@ class LoanInterestAccrual(AccountsController):
frappe.throw(_("Interest Amount or Principal Amount is mandatory"))
if not self.last_accrual_date:
self.last_accrual_date = get_last_accrual_date(self.loan)
self.last_accrual_date = get_last_accrual_date(self.loan, self.posting_date)
def on_submit(self):
self.make_gl_entries()
@@ -271,14 +271,14 @@ def make_loan_interest_accrual_entry(args):
def get_no_of_days_for_interest_accural(loan, posting_date):
last_interest_accrual_date = get_last_accrual_date(loan.name)
last_interest_accrual_date = get_last_accrual_date(loan.name, posting_date)
no_of_days = date_diff(posting_date or nowdate(), last_interest_accrual_date) + 1
return no_of_days
def get_last_accrual_date(loan):
def get_last_accrual_date(loan, posting_date):
last_posting_date = frappe.db.sql(
""" SELECT MAX(posting_date) from `tabLoan Interest Accrual`
WHERE loan = %s and docstatus = 1""",
@@ -286,12 +286,30 @@ def get_last_accrual_date(loan):
)
if last_posting_date[0][0]:
last_interest_accrual_date = last_posting_date[0][0]
# interest for last interest accrual date is already booked, so add 1 day
return add_days(last_posting_date[0][0], 1)
last_disbursement_date = get_last_disbursement_date(loan, posting_date)
if last_disbursement_date and getdate(last_disbursement_date) > getdate(
last_interest_accrual_date
):
last_interest_accrual_date = last_disbursement_date
return add_days(last_interest_accrual_date, 1)
else:
return frappe.db.get_value("Loan", loan, "disbursement_date")
def get_last_disbursement_date(loan, posting_date):
last_disbursement_date = frappe.db.get_value(
"Loan Disbursement",
{"docstatus": 1, "against_loan": loan, "posting_date": ("<", posting_date)},
"MAX(posting_date)",
)
return last_disbursement_date
def days_in_year(year):
days = 365

View File

@@ -101,7 +101,7 @@ class LoanRepayment(AccountsController):
if flt(self.total_interest_paid, precision) > flt(self.interest_payable, precision):
if not self.is_term_loan:
# get last loan interest accrual date
last_accrual_date = get_last_accrual_date(self.against_loan)
last_accrual_date = get_last_accrual_date(self.against_loan, self.posting_date)
# get posting date upto which interest has to be accrued
per_day_interest = get_per_day_interest(
@@ -722,7 +722,7 @@ def get_amounts(amounts, against_loan, posting_date):
if due_date:
pending_days = date_diff(posting_date, due_date) + 1
else:
last_accrual_date = get_last_accrual_date(against_loan_doc.name)
last_accrual_date = get_last_accrual_date(against_loan_doc.name, posting_date)
pending_days = date_diff(posting_date, last_accrual_date) + 1
if pending_days > 0:

View File

@@ -2,6 +2,7 @@
"charts": [],
"content": "[{\"id\":\"_38WStznya\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"id\":\"t7o_K__1jB\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Loan Application\",\"col\":3}},{\"id\":\"IRiNDC6w1p\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Loan\",\"col\":3}},{\"id\":\"xbbo0FYbq0\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"7ZL4Bro-Vi\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yhyioTViZ3\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"id\":\"oYFn4b1kSw\",\"type\":\"card\",\"data\":{\"card_name\":\"Loan\",\"col\":4}},{\"id\":\"vZepJF5tl9\",\"type\":\"card\",\"data\":{\"card_name\":\"Loan Processes\",\"col\":4}},{\"id\":\"k-393Mjhqe\",\"type\":\"card\",\"data\":{\"card_name\":\"Disbursement and Repayment\",\"col\":4}},{\"id\":\"6crJ0DBiBJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Loan Security\",\"col\":4}},{\"id\":\"Um5YwxVLRJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]",
"creation": "2020-03-12 16:35:55.299820",
"custom_blocks": [],
"docstatus": 0,
"doctype": "Workspace",
"for_user": "",
@@ -279,17 +280,18 @@
"type": "Link"
}
],
"modified": "2023-01-31 19:47:13.114415",
"modified": "2023-05-24 14:47:24.109945",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loans",
"number_cards": [],
"owner": "Administrator",
"parent_page": "",
"public": 1,
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
"sequence_id": 16.0,
"sequence_id": 15.0,
"shortcuts": [
{
"color": "Green",

View File

@@ -88,12 +88,14 @@ class BOMUpdateLog(Document):
boms=boms,
timeout=40000,
now=frappe.flags.in_test,
enqueue_after_commit=True,
)
else:
frappe.enqueue(
method="erpnext.manufacturing.doctype.bom_update_log.bom_update_log.process_boms_cost_level_wise",
update_doc=self,
now=frappe.flags.in_test,
enqueue_after_commit=True,
)

View File

@@ -83,7 +83,7 @@ frappe.ui.form.on('Job Card', {
// and if stock mvt for WIP is required
if (frm.doc.work_order) {
frappe.db.get_value('Work Order', frm.doc.work_order, ['skip_transfer', 'status'], (result) => {
if (result.skip_transfer === 1 || result.status == 'In Process' || frm.doc.transferred_qty > 0) {
if (result.skip_transfer === 1 || result.status == 'In Process' || frm.doc.transferred_qty > 0 || !frm.doc.items.length) {
frm.trigger("prepare_timer_buttons");
}
});
@@ -411,6 +411,16 @@ frappe.ui.form.on('Job Card', {
}
});
if (frm.doc.total_completed_qty && frm.doc.for_quantity > frm.doc.total_completed_qty) {
let flt_precision = precision('for_quantity', frm.doc);
let process_loss_qty = (
flt(frm.doc.for_quantity, flt_precision)
- flt(frm.doc.total_completed_qty, flt_precision)
);
frm.set_value('process_loss_qty', process_loss_qty);
}
refresh_field("total_completed_qty");
}
});

View File

@@ -38,6 +38,7 @@
"time_logs",
"section_break_13",
"total_completed_qty",
"process_loss_qty",
"column_break_15",
"total_time_in_mins",
"section_break_8",
@@ -435,11 +436,17 @@
"fieldname": "expected_end_date",
"fieldtype": "Datetime",
"label": "Expected End Date"
},
{
"fieldname": "process_loss_qty",
"fieldtype": "Float",
"label": "Process Loss Qty",
"read_only": 1
}
],
"is_submittable": 1,
"links": [],
"modified": "2022-11-09 15:02:44.490731",
"modified": "2023-06-09 12:04:55.534264",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Job Card",
@@ -497,4 +504,4 @@
"states": [],
"title_field": "operation",
"track_changes": 1
}
}

View File

@@ -161,7 +161,7 @@ class JobCard(Document):
self.total_completed_qty = flt(self.total_completed_qty, self.precision("total_completed_qty"))
for row in self.sub_operations:
self.total_completed_qty += row.completed_qty
self.c += row.completed_qty
def get_overlap_for(self, args, check_next_available_slot=False):
production_capacity = 1
@@ -451,6 +451,9 @@ class JobCard(Document):
},
)
def before_save(self):
self.set_process_loss()
def on_submit(self):
self.validate_transfer_qty()
self.validate_job_card()
@@ -487,19 +490,35 @@ class JobCard(Document):
)
)
if self.for_quantity and self.total_completed_qty != self.for_quantity:
precision = self.precision("total_completed_qty")
total_completed_qty = flt(
flt(self.total_completed_qty, precision) + flt(self.process_loss_qty, precision)
)
if self.for_quantity and flt(total_completed_qty, precision) != flt(
self.for_quantity, precision
):
total_completed_qty = bold(_("Total Completed Qty"))
qty_to_manufacture = bold(_("Qty to Manufacture"))
frappe.throw(
_("The {0} ({1}) must be equal to {2} ({3})").format(
total_completed_qty,
bold(self.total_completed_qty),
bold(flt(total_completed_qty, precision)),
qty_to_manufacture,
bold(self.for_quantity),
)
)
def set_process_loss(self):
precision = self.precision("total_completed_qty")
self.process_loss_qty = 0.0
if self.total_completed_qty and self.for_quantity > self.total_completed_qty:
self.process_loss_qty = flt(self.for_quantity, precision) - flt(
self.total_completed_qty, precision
)
def update_work_order(self):
if not self.work_order:
return
@@ -511,7 +530,7 @@ class JobCard(Document):
):
return
for_quantity, time_in_mins = 0, 0
for_quantity, time_in_mins, process_loss_qty = 0, 0, 0
from_time_list, to_time_list = [], []
field = "operation_id"
@@ -519,6 +538,7 @@ class JobCard(Document):
if data and len(data) > 0:
for_quantity = flt(data[0].completed_qty)
time_in_mins = flt(data[0].time_in_mins)
process_loss_qty = flt(data[0].process_loss_qty)
wo = frappe.get_doc("Work Order", self.work_order)
@@ -526,8 +546,8 @@ class JobCard(Document):
self.update_corrective_in_work_order(wo)
elif self.operation_id:
self.validate_produced_quantity(for_quantity, wo)
self.update_work_order_data(for_quantity, time_in_mins, wo)
self.validate_produced_quantity(for_quantity, process_loss_qty, wo)
self.update_work_order_data(for_quantity, process_loss_qty, time_in_mins, wo)
def update_corrective_in_work_order(self, wo):
wo.corrective_operation_cost = 0.0
@@ -542,11 +562,11 @@ class JobCard(Document):
wo.flags.ignore_validate_update_after_submit = True
wo.save()
def validate_produced_quantity(self, for_quantity, wo):
def validate_produced_quantity(self, for_quantity, process_loss_qty, wo):
if self.docstatus < 2:
return
if wo.produced_qty > for_quantity:
if wo.produced_qty > for_quantity + process_loss_qty:
first_part_msg = _(
"The {0} {1} is used to calculate the valuation cost for the finished good {2}."
).format(
@@ -561,7 +581,8 @@ class JobCard(Document):
_("{0} {1}").format(first_part_msg, second_part_msg), JobCardCancelError, title=_("Error")
)
def update_work_order_data(self, for_quantity, time_in_mins, wo):
def update_work_order_data(self, for_quantity, process_loss_qty, time_in_mins, wo):
workstation_hour_rate = frappe.get_value("Workstation", self.workstation, "hour_rate")
jc = frappe.qb.DocType("Job Card")
jctl = frappe.qb.DocType("Job Card Time Log")
@@ -581,12 +602,14 @@ class JobCard(Document):
for data in wo.operations:
if data.get("name") == self.operation_id:
data.completed_qty = for_quantity
data.process_loss_qty = process_loss_qty
data.actual_operation_time = time_in_mins
data.actual_start_time = time_data[0].start_time if time_data else None
data.actual_end_time = time_data[0].end_time if time_data else None
if data.get("workstation") != self.workstation:
# workstations can change in a job card
data.workstation = self.workstation
data.hour_rate = flt(workstation_hour_rate)
wo.flags.ignore_validate_update_after_submit = True
wo.update_operation_status()
@@ -597,7 +620,11 @@ class JobCard(Document):
def get_current_operation_data(self):
return frappe.get_all(
"Job Card",
fields=["sum(total_time_in_mins) as time_in_mins", "sum(total_completed_qty) as completed_qty"],
fields=[
"sum(total_time_in_mins) as time_in_mins",
"sum(total_completed_qty) as completed_qty",
"sum(process_loss_qty) as process_loss_qty",
],
filters={
"docstatus": 1,
"work_order": self.work_order,
@@ -651,23 +678,19 @@ class JobCard(Document):
exc=JobCardOverTransferError,
)
job_card_items_transferred_qty = _get_job_card_items_transferred_qty(ste_doc)
job_card_items_transferred_qty = _get_job_card_items_transferred_qty(ste_doc) or {}
allow_excess = frappe.db.get_single_value("Manufacturing Settings", "job_card_excess_transfer")
if job_card_items_transferred_qty:
allow_excess = frappe.db.get_single_value("Manufacturing Settings", "job_card_excess_transfer")
for row in ste_doc.items:
if not row.job_card_item:
continue
for row in ste_doc.items:
if not row.job_card_item:
continue
transferred_qty = flt(job_card_items_transferred_qty.get(row.job_card_item, 0.0))
transferred_qty = flt(job_card_items_transferred_qty.get(row.job_card_item))
if not allow_excess:
_validate_over_transfer(row, transferred_qty)
if not allow_excess:
_validate_over_transfer(row, transferred_qty)
frappe.db.set_value(
"Job Card Item", row.job_card_item, "transferred_qty", flt(transferred_qty)
)
frappe.db.set_value("Job Card Item", row.job_card_item, "transferred_qty", flt(transferred_qty))
def set_transferred_qty(self, update_status=False):
"Set total FG Qty in Job Card for which RM was transferred."
@@ -728,7 +751,7 @@ class JobCard(Document):
self.status = {0: "Open", 1: "Submitted", 2: "Cancelled"}[self.docstatus or 0]
if self.docstatus < 2:
if self.for_quantity <= self.transferred_qty:
if flt(self.for_quantity) <= flt(self.transferred_qty):
self.status = "Material Transferred"
if self.time_logs:
@@ -779,7 +802,7 @@ class JobCard(Document):
data = frappe.get_all(
"Work Order Operation",
fields=["operation", "status", "completed_qty"],
fields=["operation", "status", "completed_qty", "sequence_id"],
filters={"docstatus": 1, "parent": self.work_order, "sequence_id": ("<", self.sequence_id)},
order_by="sequence_id, idx",
)
@@ -797,6 +820,16 @@ class JobCard(Document):
OperationSequenceError,
)
if row.completed_qty < current_operation_qty:
msg = f"""The completed quantity {bold(current_operation_qty)}
of an operation {bold(self.operation)} cannot be greater
than the completed quantity {bold(row.completed_qty)}
of a previous operation
{bold(row.operation)}.
"""
frappe.throw(_(msg))
def validate_work_order(self):
if self.is_work_order_closed():
frappe.throw(_("You can't make any changes to Job Card since Work Order is closed."))

View File

@@ -5,15 +5,17 @@
from typing import Literal
import frappe
from frappe.test_runner import make_test_records
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import random_string
from frappe.utils.data import add_to_date, now
from frappe.utils.data import add_to_date, now, today
from erpnext.manufacturing.doctype.job_card.job_card import (
JobCardOverTransferError,
OperationMismatchError,
OverlapError,
make_corrective_job_card,
make_material_request,
)
from erpnext.manufacturing.doctype.job_card.job_card import (
make_stock_entry as make_stock_entry_from_jc,
@@ -342,6 +344,12 @@ class TestJobCard(FrappeTestCase):
job_card.reload()
self.assertEqual(job_card.transferred_qty, 2)
transfer_entry_2.cancel()
transfer_entry.cancel()
job_card.reload()
self.assertEqual(job_card.transferred_qty, 0.0)
def test_job_card_material_transfer_correctness(self):
"""
1. Test if only current Job Card Items are pulled in a Stock Entry against a Job Card
@@ -443,6 +451,138 @@ class TestJobCard(FrappeTestCase):
jc.docstatus = 2
assertStatus("Cancelled")
def test_job_card_material_request_and_bom_details(self):
from erpnext.stock.doctype.material_request.material_request import make_stock_entry
create_bom_with_multiple_operations()
work_order = make_wo_with_transfer_against_jc()
job_card_name = frappe.db.get_value("Job Card", {"work_order": work_order.name}, "name")
mr = make_material_request(job_card_name)
mr.schedule_date = today()
mr.submit()
ste = make_stock_entry(mr.name)
self.assertEqual(ste.purpose, "Material Transfer for Manufacture")
self.assertEqual(ste.work_order, work_order.name)
self.assertEqual(ste.job_card, job_card_name)
self.assertEqual(ste.from_bom, 1.0)
self.assertEqual(ste.bom_no, work_order.bom_no)
def test_job_card_proccess_qty_and_completed_qty(self):
from erpnext.manufacturing.doctype.routing.test_routing import (
create_routing,
setup_bom,
setup_operations,
)
from erpnext.manufacturing.doctype.work_order.work_order import (
make_stock_entry as make_stock_entry_for_wo,
)
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
operations = [
{"operation": "Test Operation A1", "workstation": "Test Workstation A", "time_in_mins": 30},
{"operation": "Test Operation B1", "workstation": "Test Workstation A", "time_in_mins": 20},
]
make_test_records("UOM")
warehouse = create_warehouse("Test Warehouse 123 for Job Card")
setup_operations(operations)
item_code = "Test Job Card Process Qty Item"
for item in [item_code, item_code + "RM 1", item_code + "RM 2"]:
if not frappe.db.exists("Item", item):
make_item(
item,
{
"item_name": item,
"stock_uom": "Nos",
"is_stock_item": 1,
},
)
routing_doc = create_routing(routing_name="Testing Route", operations=operations)
bom_doc = setup_bom(
item_code=item_code,
routing=routing_doc.name,
raw_materials=[item_code + "RM 1", item_code + "RM 2"],
source_warehouse=warehouse,
)
for row in bom_doc.items:
make_stock_entry(
item_code=row.item_code,
target=row.source_warehouse,
qty=10,
basic_rate=100,
)
wo_doc = make_wo_order_test_record(
production_item=item_code,
bom_no=bom_doc.name,
skip_transfer=1,
wip_warehouse=warehouse,
source_warehouse=warehouse,
)
for row in routing_doc.operations:
self.assertEqual(row.sequence_id, row.idx)
first_job_card = frappe.get_all(
"Job Card",
filters={"work_order": wo_doc.name, "sequence_id": 1},
fields=["name"],
order_by="sequence_id",
limit=1,
)[0].name
jc = frappe.get_doc("Job Card", first_job_card)
jc.time_logs[0].completed_qty = 8
jc.save()
jc.submit()
self.assertEqual(jc.process_loss_qty, 2)
self.assertEqual(jc.for_quantity, 10)
second_job_card = frappe.get_all(
"Job Card",
filters={"work_order": wo_doc.name, "sequence_id": 2},
fields=["name"],
order_by="sequence_id",
limit=1,
)[0].name
jc2 = frappe.get_doc("Job Card", second_job_card)
jc2.time_logs[0].completed_qty = 10
self.assertRaises(frappe.ValidationError, jc2.save)
jc2.load_from_db()
jc2.time_logs[0].completed_qty = 8
jc2.save()
jc2.submit()
self.assertEqual(jc2.for_quantity, 10)
self.assertEqual(jc2.process_loss_qty, 2)
s = frappe.get_doc(make_stock_entry_for_wo(wo_doc.name, "Manufacture", 10))
s.submit()
self.assertEqual(s.process_loss_qty, 2)
wo_doc.reload()
for row in wo_doc.operations:
self.assertEqual(row.completed_qty, 8)
self.assertEqual(row.process_loss_qty, 2)
self.assertEqual(wo_doc.produced_qty, 8)
self.assertEqual(wo_doc.process_loss_qty, 2)
self.assertEqual(wo_doc.status, "Completed")
def create_bom_with_multiple_operations():
"Create a BOM with multiple operations and Material Transfer against Job Card"

View File

@@ -451,10 +451,14 @@ frappe.ui.form.on("Material Request Plan Item", {
for_warehouse: row.warehouse
},
callback: function(r) {
let {projected_qty, actual_qty} = r.message;
if (r.message) {
let {projected_qty, actual_qty} = r.message[0];
frappe.model.set_value(cdt, cdn, 'projected_qty', projected_qty);
frappe.model.set_value(cdt, cdn, 'actual_qty', actual_qty);
frappe.model.set_value(cdt, cdn, {
'projected_qty': projected_qty,
'actual_qty': actual_qty
});
}
}
})
}

View File

@@ -35,8 +35,12 @@
"section_break_25",
"prod_plan_references",
"section_break_24",
"get_sub_assembly_items",
"combine_sub_items",
"section_break_ucc4",
"skip_available_sub_assembly_item",
"column_break_igxl",
"get_sub_assembly_items",
"section_break_g4ip",
"sub_assembly_items",
"download_materials_request_plan_section_section",
"download_materials_required",
@@ -351,12 +355,12 @@
{
"fieldname": "section_break_24",
"fieldtype": "Section Break",
"hide_border": 1
"hide_border": 1,
"label": "Sub Assembly Items"
},
{
"fieldname": "sub_assembly_items",
"fieldtype": "Table",
"label": "Sub Assembly Items",
"no_copy": 1,
"options": "Production Plan Sub Assembly Item"
},
@@ -392,13 +396,33 @@
"fieldname": "download_materials_request_plan_section_section",
"fieldtype": "Section Break",
"label": "Download Materials Request Plan Section"
},
{
"default": "0",
"description": "System consider the projected quantity to check available or will be available sub-assembly items ",
"fieldname": "skip_available_sub_assembly_item",
"fieldtype": "Check",
"label": "Skip Available Sub Assembly Items"
},
{
"fieldname": "section_break_ucc4",
"fieldtype": "Column Break",
"hide_border": 1
},
{
"fieldname": "section_break_g4ip",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_igxl",
"fieldtype": "Column Break"
}
],
"icon": "fa fa-calendar",
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-03-31 10:30:48.118932",
"modified": "2023-05-22 23:36:31.770517",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan",

View File

@@ -718,7 +718,9 @@ class ProductionPlan(Document):
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)
warehouse = row.warehouse if self.skip_available_sub_assembly_item else None
get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty, self.company, warehouse=warehouse)
self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
sub_assembly_items_store.extend(bom_data)
@@ -894,7 +896,9 @@ def download_raw_materials(doc, warehouses=None):
build_csv_response(item_list, doc.name)
def get_exploded_items(item_details, company, bom_no, include_non_stock_items, planned_qty=1):
def get_exploded_items(
item_details, company, bom_no, include_non_stock_items, planned_qty=1, doc=None
):
bei = frappe.qb.DocType("BOM Explosion Item")
bom = frappe.qb.DocType("BOM")
item = frappe.qb.DocType("Item")
@@ -1271,6 +1275,12 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
include_safety_stock = doc.get("include_safety_stock")
so_item_details = frappe._dict()
sub_assembly_items = {}
if doc.get("skip_available_sub_assembly_item"):
for d in doc.get("sub_assembly_items"):
sub_assembly_items.setdefault((d.get("production_item"), d.get("bom_no")), d.get("qty"))
for data in po_items:
if not data.get("include_exploded_items") and doc.get("sub_assembly_items"):
data["include_exploded_items"] = 1
@@ -1296,10 +1306,24 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
frappe.throw(_("For row {0}: Enter Planned Qty").format(data.get("idx")))
if bom_no:
if data.get("include_exploded_items") and include_subcontracted_items:
if (
data.get("include_exploded_items")
and doc.get("sub_assembly_items")
and doc.get("skip_available_sub_assembly_item")
):
item_details = get_raw_materials_of_sub_assembly_items(
item_details,
company,
bom_no,
include_non_stock_items,
sub_assembly_items,
planned_qty=planned_qty,
)
elif data.get("include_exploded_items") and include_subcontracted_items:
# fetch exploded items from BOM
item_details = get_exploded_items(
item_details, company, bom_no, include_non_stock_items, planned_qty=planned_qty
item_details, company, bom_no, include_non_stock_items, planned_qty=planned_qty, doc=doc
)
else:
item_details = get_subitems(
@@ -1456,12 +1480,22 @@ def get_item_data(item_code):
}
def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0):
def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, company, warehouse=None, indent=0):
data = get_bom_children(parent=bom_no)
for d in data:
if d.expandable:
parent_item_code = frappe.get_cached_value("BOM", bom_no, "item")
stock_qty = (d.stock_qty / d.parent_bom_qty) * flt(to_produce_qty)
if warehouse:
bin_dict = get_bin_details(d, company, for_warehouse=warehouse)
if bin_dict and bin_dict[0].projected_qty > 0:
if bin_dict[0].projected_qty > stock_qty:
continue
else:
stock_qty = stock_qty - bin_dict[0].projected_qty
bom_data.append(
frappe._dict(
{
@@ -1481,7 +1515,7 @@ def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, indent=0):
)
if d.value:
get_sub_assembly_items(d.value, bom_data, stock_qty, indent=indent + 1)
get_sub_assembly_items(d.value, bom_data, stock_qty, company, warehouse, indent=indent + 1)
def set_default_warehouses(row, default_warehouses):
@@ -1500,7 +1534,7 @@ def get_reserved_qty_for_production_plan(item_code, warehouse):
frappe.qb.from_(table)
.inner_join(child)
.on(table.name == child.parent)
.select(Sum(child.quantity * IfNull(child.conversion_factor, 1.0)))
.select(Sum(child.required_bom_qty * IfNull(child.conversion_factor, 1.0)))
.where(
(table.docstatus == 1)
& (child.item_code == item_code)
@@ -1518,4 +1552,72 @@ def get_reserved_qty_for_production_plan(item_code, warehouse):
get_reserved_qty_for_production(item_code, warehouse, check_production_plan=True)
)
if reserved_qty_for_production > reserved_qty_for_production_plan:
return 0.0
return reserved_qty_for_production_plan - reserved_qty_for_production
def get_raw_materials_of_sub_assembly_items(
item_details, company, bom_no, include_non_stock_items, sub_assembly_items, planned_qty=1
):
bei = frappe.qb.DocType("BOM Item")
bom = frappe.qb.DocType("BOM")
item = frappe.qb.DocType("Item")
item_default = frappe.qb.DocType("Item Default")
item_uom = frappe.qb.DocType("UOM Conversion Detail")
items = (
frappe.qb.from_(bei)
.join(bom)
.on(bom.name == bei.parent)
.join(item)
.on(item.name == bei.item_code)
.left_join(item_default)
.on((item_default.parent == item.name) & (item_default.company == company))
.left_join(item_uom)
.on((item.name == item_uom.parent) & (item_uom.uom == item.purchase_uom))
.select(
(IfNull(Sum(bei.stock_qty / IfNull(bom.quantity, 1)), 0) * planned_qty).as_("qty"),
item.item_name,
item.name.as_("item_code"),
bei.description,
bei.stock_uom,
bei.bom_no,
item.min_order_qty,
bei.source_warehouse,
item.default_material_request_type,
item.min_order_qty,
item_default.default_warehouse,
item.purchase_uom,
item_uom.conversion_factor,
item.safety_stock,
)
.where(
(bei.docstatus == 1)
& (bom.name == bom_no)
& (item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1)
)
.groupby(bei.item_code, bei.stock_uom)
).run(as_dict=True)
for item in items:
key = (item.item_code, item.bom_no)
if item.bom_no and key in sub_assembly_items:
planned_qty = flt(sub_assembly_items[key])
get_raw_materials_of_sub_assembly_items(
item_details,
company,
item.bom_no,
include_non_stock_items,
sub_assembly_items,
planned_qty=planned_qty,
)
else:
if not item.conversion_factor and item.purchase_uom:
item.conversion_factor = get_uom_conversion_factor(item.item_code, item.purchase_uom)
item_details.setdefault(item.get("item_code"), item)
return item_details

View File

@@ -926,6 +926,50 @@ class TestProductionPlan(FrappeTestCase):
self.assertEqual(after_qty, before_qty)
def test_skip_available_qty_for_sub_assembly_items(self):
from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
bom_tree = {
"Fininshed Goods1 For SUB Test": {
"SubAssembly1 For SUB Test": {"ChildPart1 For SUB Test": {}},
"SubAssembly2 For SUB Test": {},
}
}
parent_bom = create_nested_bom(bom_tree, prefix="")
plan = create_production_plan(
item_code=parent_bom.item,
planned_qty=10,
ignore_existing_ordered_qty=1,
do_not_submit=1,
skip_available_sub_assembly_item=1,
warehouse="_Test Warehouse - _TC",
)
make_stock_entry(
item_code="SubAssembly1 For SUB Test",
qty=5,
rate=100,
target="_Test Warehouse - _TC",
)
self.assertTrue(plan.skip_available_sub_assembly_item)
plan.get_sub_assembly_items()
for row in plan.sub_assembly_items:
if row.production_item == "SubAssembly1 For SUB Test":
self.assertEqual(row.qty, 5)
mr_items = get_items_for_material_requests(plan.as_dict())
for row in mr_items:
row = frappe._dict(row)
if row.item_code == "ChildPart1 For SUB Test":
self.assertEqual(row.quantity, 5)
if row.item_code == "SubAssembly2 For SUB Test":
self.assertEqual(row.quantity, 10)
def create_production_plan(**args):
"""
@@ -945,6 +989,7 @@ def create_production_plan(**args):
"include_subcontracted_items": args.include_subcontracted_items or 0,
"ignore_existing_ordered_qty": args.ignore_existing_ordered_qty or 0,
"get_items_from": "Sales Order",
"skip_available_sub_assembly_item": args.skip_available_sub_assembly_item or 0,
}
)
@@ -958,6 +1003,7 @@ def create_production_plan(**args):
"planned_qty": args.planned_qty or 1,
"planned_start_date": args.planned_start_date or now_datetime(),
"stock_uom": args.stock_uom or "Nos",
"warehouse": args.warehouse,
},
)

View File

@@ -28,7 +28,11 @@
"uom",
"stock_uom",
"column_break_22",
"description"
"description",
"section_break_4rxf",
"actual_qty",
"column_break_xfhm",
"projected_qty"
],
"fields": [
{
@@ -183,12 +187,34 @@
"fieldtype": "Datetime",
"in_list_view": 1,
"label": "Schedule Date"
},
{
"fieldname": "section_break_4rxf",
"fieldtype": "Section Break"
},
{
"fieldname": "actual_qty",
"fieldtype": "Float",
"label": "Actual Qty",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "column_break_xfhm",
"fieldtype": "Column Break"
},
{
"fieldname": "projected_qty",
"fieldtype": "Float",
"label": "Projected Qty",
"no_copy": 1,
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2022-11-28 13:50:15.116082",
"modified": "2023-05-22 17:52:34.708879",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan Sub Assembly Item",

View File

@@ -141,6 +141,7 @@ def setup_bom(**args):
routing=args.routing,
with_operations=1,
currency=args.currency,
source_warehouse=args.source_warehouse,
)
else:
bom_doc = frappe.get_doc("BOM", name)

View File

@@ -891,7 +891,7 @@ class TestWorkOrder(FrappeTestCase):
self.assertEqual(se.process_loss_qty, 1)
wo.load_from_db()
self.assertEqual(wo.status, "In Process")
self.assertEqual(wo.status, "Completed")
@timeout(seconds=60)
def test_job_card_scrap_item(self):

View File

@@ -139,7 +139,7 @@ frappe.ui.form.on("Work Order", {
}
if (frm.doc.status != "Closed") {
if (frm.doc.docstatus === 1
if (frm.doc.docstatus === 1 && frm.doc.status !== "Completed"
&& frm.doc.operations && frm.doc.operations.length) {
const not_completed = frm.doc.operations.filter(d => {
@@ -256,6 +256,12 @@ frappe.ui.form.on("Work Order", {
label: __('Batch Size'),
read_only: 1
},
{
fieldtype: 'Int',
fieldname: 'sequence_id',
label: __('Sequence Id'),
read_only: 1
},
],
data: operations_data,
in_place_edit: true,
@@ -280,8 +286,8 @@ frappe.ui.form.on("Work Order", {
var pending_qty = 0;
frm.doc.operations.forEach(data => {
if(data.completed_qty != frm.doc.qty) {
pending_qty = frm.doc.qty - flt(data.completed_qty);
if(data.completed_qty + data.process_loss_qty != frm.doc.qty) {
pending_qty = frm.doc.qty - flt(data.completed_qty) - flt(data.process_loss_qty);
if (pending_qty) {
dialog.fields_dict.operations.df.data.push({
@@ -290,7 +296,8 @@ frappe.ui.form.on("Work Order", {
'workstation': data.workstation,
'batch_size': data.batch_size,
'qty': pending_qty,
'pending_qty': pending_qty
'pending_qty': pending_qty,
'sequence_id': data.sequence_id
});
}
}

View File

@@ -47,8 +47,8 @@
"required_items_section",
"materials_and_operations_tab",
"operations_section",
"operations",
"transfer_material_against",
"operations",
"time",
"planned_start_date",
"planned_end_date",
@@ -331,7 +331,6 @@
"label": "Expected Delivery Date"
},
{
"collapsible": 1,
"fieldname": "operations_section",
"fieldtype": "Section Break",
"label": "Operations",
@@ -599,7 +598,7 @@
"image_field": "image",
"is_submittable": 1,
"links": [],
"modified": "2023-04-06 12:35:12.149827",
"modified": "2023-06-09 13:20:09.154362",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order",

View File

@@ -249,7 +249,9 @@ class WorkOrder(Document):
status = "Not Started"
if flt(self.material_transferred_for_manufacturing) > 0:
status = "In Process"
if flt(self.produced_qty) >= flt(self.qty):
total_qty = flt(self.produced_qty) + flt(self.process_loss_qty)
if flt(total_qty) >= flt(self.qty):
status = "Completed"
else:
status = "Cancelled"
@@ -736,13 +738,15 @@ class WorkOrder(Document):
max_allowed_qty_for_wo = flt(self.qty) + (allowance_percentage / 100 * flt(self.qty))
for d in self.get("operations"):
if not d.completed_qty:
precision = d.precision("completed_qty")
qty = flt(d.completed_qty, precision) + flt(d.process_loss_qty, precision)
if not qty:
d.status = "Pending"
elif flt(d.completed_qty) < flt(self.qty):
elif flt(qty) < flt(self.qty):
d.status = "Work in Progress"
elif flt(d.completed_qty) == flt(self.qty):
elif flt(qty) == flt(self.qty):
d.status = "Completed"
elif flt(d.completed_qty) <= max_allowed_qty_for_wo:
elif flt(qty) <= max_allowed_qty_for_wo:
d.status = "Completed"
else:
frappe.throw(_("Completed Qty cannot be greater than 'Qty to Manufacture'"))

View File

@@ -2,12 +2,14 @@
"actions": [],
"creation": "2014-10-16 14:35:41.950175",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"details",
"operation",
"status",
"completed_qty",
"process_loss_qty",
"column_break_4",
"bom",
"workstation_type",
@@ -36,6 +38,7 @@
"fieldtype": "Section Break"
},
{
"columns": 2,
"fieldname": "operation",
"fieldtype": "Link",
"in_list_view": 1,
@@ -46,6 +49,7 @@
"reqd": 1
},
{
"columns": 2,
"fieldname": "bom",
"fieldtype": "Link",
"in_list_view": 1,
@@ -62,7 +66,7 @@
"oldfieldtype": "Text"
},
{
"columns": 1,
"columns": 2,
"description": "Operation completed for how many finished goods?",
"fieldname": "completed_qty",
"fieldtype": "Float",
@@ -80,6 +84,7 @@
"options": "Pending\nWork in Progress\nCompleted"
},
{
"columns": 1,
"fieldname": "workstation",
"fieldtype": "Link",
"in_list_view": 1,
@@ -115,7 +120,7 @@
"fieldname": "time_in_mins",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Operation Time",
"label": "Time",
"oldfieldname": "time_in_mins",
"oldfieldtype": "Currency",
"reqd": 1
@@ -203,12 +208,21 @@
"fieldtype": "Link",
"label": "Workstation Type",
"options": "Workstation Type"
},
{
"columns": 2,
"fieldname": "process_loss_qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Process Loss Qty",
"no_copy": 1,
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2022-11-09 01:37:56.563068",
"modified": "2023-06-09 14:03:01.612909",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order Operation",

View File

@@ -1,13 +1,15 @@
{
"charts": [],
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Card\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
"content": "[{\"id\":\"csBCiDglCE\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"id\":\"xit0dg7KvY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"id\":\"LRhGV9GAov\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"id\":\"69KKosI6Hg\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"id\":\"PwndxuIpB3\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Card\",\"col\":3}},{\"id\":\"OaiDqTT03Y\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"id\":\"OtMcArFRa5\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"id\":\"76yYsI5imF\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"id\":\"bN_6tHS-Ct\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yVEFZMqVwd\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"id\":\"rwrmsTI58-\",\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"id\":\"6dnsyX-siZ\",\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"id\":\"CIq-v5f5KC\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"8RRiQeYr0G\",\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"id\":\"Pu8z7-82rT\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
"creation": "2020-03-02 17:11:37.032604",
"custom_blocks": [],
"docstatus": 0,
"doctype": "Workspace",
"for_user": "",
"hide_custom": 0,
"icon": "organization",
"idx": 0,
"is_hidden": 0,
"label": "Manufacturing",
"links": [
{
@@ -243,7 +245,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Bill of Materials",
"link_count": 15,
"link_count": 6,
"onboard": 0,
"type": "Card Break"
},
@@ -312,117 +314,20 @@
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Work Order",
"hidden": 0,
"is_query_report": 1,
"label": "Production Planning Report",
"link_count": 0,
"link_to": "Production Planning Report",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Quality Inspection",
"hidden": 0,
"is_query_report": 1,
"label": "Work Order Summary",
"link_count": 0,
"link_to": "Work Order Summary",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Downtime Entry",
"hidden": 0,
"is_query_report": 1,
"label": "Quality Inspection Summary",
"link_count": 0,
"link_to": "Quality Inspection Summary",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Job Card",
"hidden": 0,
"is_query_report": 1,
"label": "Downtime Analysis",
"link_count": 0,
"link_to": "Downtime Analysis",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "BOM",
"hidden": 0,
"is_query_report": 1,
"label": "Job Card Summary",
"link_count": 0,
"link_to": "Job Card Summary",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "BOM",
"hidden": 0,
"is_query_report": 1,
"label": "BOM Search",
"link_count": 0,
"link_to": "BOM Search",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Work Order",
"hidden": 0,
"is_query_report": 1,
"label": "BOM Stock Report",
"link_count": 0,
"link_to": "BOM Stock Report",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "BOM",
"hidden": 0,
"is_query_report": 1,
"label": "Production Analytics",
"link_count": 0,
"link_to": "Production Analytics",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "BOM Operations Time",
"link_count": 0,
"link_to": "BOM Operations Time",
"link_type": "Report",
"onboard": 0,
"type": "Link"
}
],
"modified": "2022-11-14 14:53:34.616862",
"modified": "2023-05-27 16:41:04.776115",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Manufacturing",
"number_cards": [],
"owner": "Administrator",
"parent_page": "",
"public": 1,
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
"sequence_id": 17.0,
"sequence_id": 8.0,
"shortcuts": [
{
"color": "Grey",

View File

@@ -331,3 +331,5 @@ execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_h
# below migration patches should always run last
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
erpnext.patches.v14_0.update_company_in_ldc
erpnext.patches.v14_0.set_packed_qty_in_draft_delivery_notes
erpnext.patches.v14_0.cleanup_workspaces

View File

@@ -7,4 +7,6 @@ def execute():
frappe.reload_doc("manufacturing", "doctype", "work_order")
frappe.reload_doc("manufacturing", "doctype", "work_order_item")
frappe.db.sql("""UPDATE `tabWork Order Item` SET amount = rate * required_qty""")
frappe.db.sql(
"""UPDATE `tabWork Order Item` SET amount = ifnull(rate, 0.0) * ifnull(required_qty, 0.0)"""
)

View File

@@ -0,0 +1,9 @@
import frappe
def execute():
for ws in ["Retail", "Utilities"]:
frappe.delete_doc_if_exists("Workspace", ws)
for ws in ["Integrations", "Settings"]:
frappe.db.set_value("Workspace", ws, "public", 0)

View File

@@ -0,0 +1,60 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe.query_builder.functions import Sum
def execute():
ps = frappe.qb.DocType("Packing Slip")
dn = frappe.qb.DocType("Delivery Note")
ps_item = frappe.qb.DocType("Packing Slip Item")
ps_details = (
frappe.qb.from_(ps)
.join(ps_item)
.on(ps.name == ps_item.parent)
.join(dn)
.on(ps.delivery_note == dn.name)
.select(
dn.name.as_("delivery_note"),
ps_item.item_code.as_("item_code"),
Sum(ps_item.qty).as_("packed_qty"),
)
.where((ps.docstatus == 1) & (dn.docstatus == 0))
.groupby(dn.name, ps_item.item_code)
).run(as_dict=True)
if ps_details:
dn_list = set()
item_code_list = set()
for ps_detail in ps_details:
dn_list.add(ps_detail.delivery_note)
item_code_list.add(ps_detail.item_code)
dn_item = frappe.qb.DocType("Delivery Note Item")
dn_item_query = (
frappe.qb.from_(dn_item)
.select(
dn.parent.as_("delivery_note"),
dn_item.name,
dn_item.item_code,
dn_item.qty,
)
.where((dn_item.parent.isin(dn_list)) & (dn_item.item_code.isin(item_code_list)))
)
dn_details = frappe._dict()
for r in dn_item_query.run(as_dict=True):
dn_details.setdefault((r.delivery_note, r.item_code), frappe._dict()).setdefault(r.name, r.qty)
for ps_detail in ps_details:
dn_items = dn_details.get((ps_detail.delivery_note, ps_detail.item_code))
if dn_items:
remaining_qty = ps_detail.packed_qty
for name, qty in dn_items.items():
if remaining_qty > 0:
row_packed_qty = min(qty, remaining_qty)
frappe.db.set_value("Delivery Note Item", name, "packed_qty", row_packed_qty)
remaining_qty -= row_packed_qty

View File

@@ -25,20 +25,38 @@ frappe.listview_settings['Task'] = {
}
return [__(doc.status), colors[doc.status], "status,=," + doc.status];
},
gantt_custom_popup_html: function(ganttobj, task) {
var html = `<h5><a style="text-decoration:underline"\
href="/app/task/${ganttobj.id}""> ${ganttobj.name} </a></h5>`;
gantt_custom_popup_html: function (ganttobj, task) {
let html = `
<a class="text-white mb-2 inline-block cursor-pointer"
href="/app/task/${ganttobj.id}"">
${ganttobj.name}
</a>
`;
if(task.project) html += `<p>Project: ${task.project}</p>`;
html += `<p>Progress: ${ganttobj.progress}</p>`;
if (task.project) {
html += `<p class="mb-1">${__("Project")}:
<a class="text-white inline-block"
href="/app/project/${task.project}"">
${task.project}
</a>
</p>`;
}
html += `<p class="mb-1">
${__("Progress")}:
<span class="text-white">${ganttobj.progress}%</span>
</p>`;
if(task._assign_list) {
html += task._assign_list.reduce(
(html, user) => html + frappe.avatar(user)
, '');
if (task._assign) {
const assign_list = JSON.parse(task._assign);
const assignment_wrapper = `
<span>Assigned to:</span>
<span class="text-white">
${assign_list.map((user) => frappe.user_info(user).fullname).join(", ")}
</span>
`;
html += assignment_wrapper;
}
return html;
}
return `<div class="p-3" style="min-width: 220px">${html}</div>`;
},
};

View File

@@ -7,12 +7,14 @@
],
"content": "[{\"type\":\"chart\",\"data\":{\"chart_name\":\"Open Projects\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Task\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Timesheet\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project Billing Summary\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Projects\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Time Tracking\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
"creation": "2020-03-02 15:46:04.874669",
"custom_blocks": [],
"docstatus": 0,
"doctype": "Workspace",
"for_user": "",
"hide_custom": 0,
"icon": "project",
"idx": 0,
"is_hidden": 0,
"label": "Projects",
"links": [
{
@@ -190,17 +192,18 @@
"type": "Link"
}
],
"modified": "2022-10-11 22:39:10.436311",
"modified": "2023-05-24 14:47:23.179860",
"modified_by": "Administrator",
"module": "Projects",
"name": "Projects",
"number_cards": [],
"owner": "Administrator",
"parent_page": "",
"public": 1,
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
"sequence_id": 20.0,
"sequence_id": 11.0,
"shortcuts": [
{
"color": "Blue",

View File

@@ -91,6 +91,12 @@ frappe.ui.form.on("Sales Invoice", {
});
frappe.ui.form.on('Purchase Invoice', {
setup: (frm) => {
frm.make_methods = {
'Landed Cost Voucher': function () { frm.trigger('create_landedcost_voucher') },
}
},
mode_of_payment: function(frm) {
get_payment_mode_account(frm, frm.doc.mode_of_payment, function(account){
frm.set_value('cash_bank_account', account);
@@ -99,6 +105,20 @@ frappe.ui.form.on('Purchase Invoice', {
payment_terms_template: function() {
cur_frm.trigger("disable_due_date");
},
create_landedcost_voucher: function (frm) {
let lcv = frappe.model.get_new_doc('Landed Cost Voucher');
lcv.company = frm.doc.company;
let lcv_receipt = frappe.model.get_new_doc('Landed Cost Purchase Invoice');
lcv_receipt.receipt_document_type = 'Purchase Invoice';
lcv_receipt.receipt_document = frm.doc.name;
lcv_receipt.supplier = frm.doc.supplier;
lcv_receipt.grand_total = frm.doc.grand_total;
lcv.purchase_receipts = [lcv_receipt];
frappe.set_route("Form", lcv.doctype, lcv.name);
}
});

View File

@@ -805,11 +805,13 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments {
);
}
this.frm.doc.payments.find(pay => {
if (pay.default) {
pay.amount = total_amount_to_pay;
}
});
if(!this.frm.doc.is_return){
this.frm.doc.payments.find(payment => {
if (payment.default) {
payment.amount = total_amount_to_pay;
}
});
}
this.frm.refresh_fields();
}

View File

@@ -494,7 +494,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
},
() => {
// for internal customer instead of pricing rule directly apply valuation rate on item
if (me.frm.doc.is_internal_customer || me.frm.doc.is_internal_supplier) {
if ((me.frm.doc.is_internal_customer || me.frm.doc.is_internal_supplier) && me.frm.doc.represents_company === me.frm.doc.company) {
me.get_incoming_rate(item, me.frm.posting_date, me.frm.posting_time,
me.frm.doc.doctype, me.frm.doc.company);
} else {

View File

@@ -16,8 +16,8 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
|| (frm.doc.party_name && in_list(['Quotation', 'Opportunity'], frm.doc.doctype))) {
let party_type = "Customer";
if (frm.doc.quotation_to && frm.doc.quotation_to === "Lead") {
party_type = "Lead";
if (frm.doc.quotation_to && in_list(["Lead", "Prospect"], frm.doc.quotation_to)) {
party_type = frm.doc.quotation_to;
}
args = {

View File

@@ -2,12 +2,14 @@
"charts": [],
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Quality Goal\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Quality Procedure\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Quality Inspection\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Quality Review\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Quality Action\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Non Conformance\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Goal and Procedure\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Feedback\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Meeting\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Review and Action\",\"col\":4}}]",
"creation": "2020-03-02 15:49:28.632014",
"custom_blocks": [],
"docstatus": 0,
"doctype": "Workspace",
"for_user": "",
"hide_custom": 0,
"icon": "quality",
"idx": 0,
"is_hidden": 0,
"label": "Quality",
"links": [
{
@@ -142,16 +144,18 @@
"type": "Link"
}
],
"modified": "2022-01-13 17:42:20.105187",
"modified": "2023-05-24 14:47:22.597974",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality",
"number_cards": [],
"owner": "Administrator",
"parent_page": "",
"public": 1,
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
"sequence_id": 21.0,
"sequence_id": 9.0,
"shortcuts": [
{
"color": "Grey",

View File

@@ -13,7 +13,7 @@ frappe.ui.form.on('Quotation', {
frm.set_query("quotation_to", function() {
return{
"filters": {
"name": ["in", ["Customer", "Lead"]],
"name": ["in", ["Customer", "Lead", "Prospect"]],
}
}
});
@@ -160,19 +160,16 @@ erpnext.selling.QuotationController = class QuotationController extends erpnext.
}
set_dynamic_field_label(){
if (this.frm.doc.quotation_to == "Customer")
{
if (this.frm.doc.quotation_to == "Customer") {
this.frm.set_df_property("party_name", "label", "Customer");
this.frm.fields_dict.party_name.get_query = null;
}
if (this.frm.doc.quotation_to == "Lead")
{
} else if (this.frm.doc.quotation_to == "Lead") {
this.frm.set_df_property("party_name", "label", "Lead");
this.frm.fields_dict.party_name.get_query = function() {
return{ query: "erpnext.controllers.queries.lead_query" }
}
} else if (this.frm.doc.quotation_to == "Prospect") {
this.frm.set_df_property("party_name", "label", "Prospect");
}
}

View File

@@ -288,7 +288,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
)
# sales team
for d in customer.get("sales_team"):
for d in customer.get("sales_team") or []:
target.append(
"sales_team",
{
@@ -299,6 +299,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
)
target.flags.ignore_permissions = ignore_permissions
target.delivery_date = nowdate()
target.run_method("set_missing_values")
target.run_method("calculate_taxes_and_totals")
@@ -306,6 +307,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
balance_qty = obj.qty - ordered_items.get(obj.item_code, 0.0)
target.qty = balance_qty if balance_qty > 0 else 0
target.stock_qty = flt(target.qty) * flt(obj.conversion_factor)
target.delivery_date = nowdate()
if obj.against_blanket_order:
target.against_blanket_order = obj.against_blanket_order

View File

@@ -60,9 +60,9 @@ class TestQuotation(FrappeTestCase):
sales_order = make_sales_order(quotation.name)
sales_order.currency = "USD"
sales_order.conversion_rate = 20.0
sales_order.delivery_date = "2019-01-01"
sales_order.naming_series = "_T-Quotation-"
sales_order.transaction_date = nowdate()
sales_order.delivery_date = nowdate()
sales_order.insert()
self.assertEqual(sales_order.currency, "USD")
@@ -644,8 +644,6 @@ def make_quotation(**args):
},
)
qo.delivery_date = add_days(qo.transaction_date, 10)
if not args.do_not_save:
qo.insert()
if not args.do_not_submit:

View File

@@ -264,7 +264,7 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
}
}
// payment request
if(flt(doc.per_billed)<100) {
if(flt(doc.per_billed, precision('per_billed', doc)) < 100 + frappe.boot.sysdefaults.over_billing_allowance) {
this.frm.add_custom_button(__('Payment Request'), () => this.make_payment_request(), __('Create'));
this.frm.add_custom_button(__('Payment'), () => this.make_payment_entry(), __('Create'));
}

View File

@@ -158,7 +158,8 @@ class SalesOrder(SellingController):
frappe.msgprint(
_("Expected Delivery Date should be after Sales Order Date"),
indicator="orange",
title=_("Warning"),
title=_("Invalid Delivery Date"),
raise_exception=True,
)
else:
frappe.throw(_("Please enter Delivery Date"))
@@ -217,6 +218,7 @@ class SalesOrder(SellingController):
frappe.throw(_("Quotation {0} is cancelled").format(quotation))
doc.set_status(update=True)
doc.update_opportunity("Converted" if flag == "submit" else "Quotation")
def validate_drop_ship(self):
for d in self.get("items"):
@@ -398,10 +400,17 @@ class SalesOrder(SellingController):
def update_picking_status(self):
total_picked_qty = 0.0
total_qty = 0.0
per_picked = 0.0
for so_item in self.items:
total_picked_qty += flt(so_item.picked_qty)
total_qty += flt(so_item.stock_qty)
per_picked = total_picked_qty / total_qty * 100
if cint(
frappe.get_cached_value("Item", so_item.item_code, "is_stock_item")
) or self.has_product_bundle(so_item.item_code):
total_picked_qty += flt(so_item.picked_qty)
total_qty += flt(so_item.stock_qty)
if total_picked_qty and total_qty:
per_picked = total_picked_qty / total_qty * 100
self.db_set("per_picked", flt(per_picked), update_modified=False)

View File

@@ -2,9 +2,11 @@
"creation": "2021-11-23 12:00:36.138824",
"docstatus": 0,
"doctype": "Form Tour",
"first_document": 0,
"idx": 0,
"include_name_field": 0,
"is_standard": 1,
"modified": "2021-11-23 12:02:48.010298",
"modified": "2023-05-23 12:51:48.684517",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation",
@@ -14,51 +16,43 @@
"steps": [
{
"description": "Select a customer or lead for whom this quotation is being prepared. Let's select a Customer.",
"field": "",
"fieldname": "quotation_to",
"fieldtype": "Link",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Quotation To",
"parent_field": "",
"position": "Right",
"title": "Quotation To"
},
{
"description": "Select a specific Customer to whom this quotation will be sent.",
"field": "",
"fieldname": "party_name",
"fieldtype": "Dynamic Link",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Party",
"parent_field": "",
"position": "Right",
"title": "Party"
},
{
"child_doctype": "Quotation Item",
"description": "Select an item for which you will be quoting a price.",
"field": "",
"fieldname": "items",
"fieldtype": "Table",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Items",
"parent_field": "",
"parent_fieldname": "items",
"position": "Bottom",
"title": "Items"
},
{
"description": "You can select pre-populated Sales Taxes and Charges from here.",
"field": "",
"fieldname": "taxes",
"fieldtype": "Table",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Sales Taxes and Charges",
"parent_field": "",
"position": "Bottom",
"title": "Sales Taxes and Charges"
}

View File

@@ -299,7 +299,8 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran
}
batch_no(doc, cdt, cdn) {
var me = this;
super.batch_no(doc, cdt, cdn);
var item = frappe.get_doc(cdt, cdn);
if (item.serial_no) {
@@ -378,10 +379,6 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran
}
}
batch_no(doc, cdt, cdn) {
super.batch_no(doc, cdt, cdn);
}
qty(doc, cdt, cdn) {
super.qty(doc, cdt, cdn);

View File

@@ -1,123 +0,0 @@
{
"charts": [],
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Point Of Sale\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings & Configurations\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Loyalty Program\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Opening & Closing\",\"col\":4}}]",
"creation": "2020-03-02 17:18:32.505616",
"docstatus": 0,
"doctype": "Workspace",
"for_user": "",
"hide_custom": 0,
"icon": "retail",
"idx": 0,
"label": "Retail",
"links": [
{
"hidden": 0,
"is_query_report": 0,
"label": "Settings & Configurations",
"link_count": 2,
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Point-of-Sale Profile",
"link_count": 0,
"link_to": "POS Profile",
"link_type": "DocType",
"onboard": 1,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "POS Settings",
"link_count": 0,
"link_to": "POS Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Loyalty Program",
"link_count": 2,
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Loyalty Program",
"link_count": 0,
"link_to": "Loyalty Program",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "Loyalty Point Entry",
"link_count": 0,
"link_to": "Loyalty Point Entry",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
"label": "Opening & Closing",
"link_count": 2,
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "POS Opening Entry",
"link_count": 0,
"link_to": "POS Opening Entry",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
"label": "POS Closing Entry",
"link_count": 0,
"link_to": "POS Closing Entry",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
"modified": "2022-01-13 18:07:56.711095",
"modified_by": "Administrator",
"module": "Selling",
"name": "Retail",
"owner": "Administrator",
"parent_page": "",
"public": 1,
"restrict_to_domain": "Retail",
"roles": [],
"sequence_id": 22.0,
"shortcuts": [
{
"doc_view": "",
"label": "Point Of Sale",
"link_to": "point-of-sale",
"type": "Page"
}
],
"title": "Retail"
}

View File

@@ -5,14 +5,16 @@
"label": "Sales Order Trends"
}
],
"content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Selling\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Sales Order Trends\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Quick Access</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order Analysis\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Selling\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Items and Pricing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]",
"content": "[{\"id\":\"ow595dYDrI\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Selling\",\"col\":12}},{\"id\":\"vBSf8Vi9U8\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Sales Order Trends\",\"col\":12}},{\"id\":\"aW2i5R5GRP\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"1it3dCOnm6\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Quick Access</b></span>\",\"col\":12}},{\"id\":\"x7pLl-spS4\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"id\":\"SSGrXWmY-H\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order\",\"col\":3}},{\"id\":\"-5J_yLxDaS\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"id\":\"6YEYpnIBKV\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Point of Sale\",\"col\":3}},{\"id\":\"c_GjZuZ2oN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"oNjjNbnUHp\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"0BcePLg0g1\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"id\":\"uze5dJ1ipL\",\"type\":\"card\",\"data\":{\"card_name\":\"Selling\",\"col\":4}},{\"id\":\"3j2fYwMAkq\",\"type\":\"card\",\"data\":{\"card_name\":\"Point of Sale\",\"col\":4}},{\"id\":\"xImm8NepFt\",\"type\":\"card\",\"data\":{\"card_name\":\"Items and Pricing\",\"col\":4}},{\"id\":\"6MjIe7KCQo\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"lBu2EKgmJF\",\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"id\":\"1ARHrjg4kI\",\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]",
"creation": "2020-01-28 11:49:12.092882",
"custom_blocks": [],
"docstatus": 0,
"doctype": "Workspace",
"for_user": "",
"hide_custom": 0,
"icon": "sell",
"idx": 0,
"is_hidden": 0,
"label": "Selling",
"links": [
{
@@ -317,140 +319,68 @@
{
"hidden": 0,
"is_query_report": 0,
"label": "Other Reports",
"link_count": 12,
"label": "Point of Sale",
"link_count": 6,
"onboard": 0,
"type": "Card Break"
},
{
"dependencies": "Lead",
"hidden": 0,
"is_query_report": 1,
"label": "Lead Details",
"is_query_report": 0,
"label": "Point-of-Sale Profile",
"link_count": 0,
"link_to": "Lead Details",
"link_type": "Report",
"link_to": "POS Profile",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Address",
"hidden": 0,
"is_query_report": 1,
"label": "Customer Addresses And Contacts",
"is_query_report": 0,
"label": "POS Settings",
"link_count": 0,
"link_to": "Address And Contacts",
"link_type": "Report",
"link_to": "POS Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Item",
"hidden": 0,
"is_query_report": 1,
"label": "Available Stock for Packing Items",
"is_query_report": 0,
"label": "POS Opening Entry",
"link_count": 0,
"link_to": "Available Stock for Packing Items",
"link_type": "Report",
"link_to": "POS Opening Entry",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Sales Order",
"hidden": 0,
"is_query_report": 1,
"label": "Pending SO Items For Purchase Request",
"is_query_report": 0,
"label": "POS Closing Entry",
"link_count": 0,
"link_to": "Pending SO Items For Purchase Request",
"link_type": "Report",
"link_to": "POS Closing Entry",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Delivery Note",
"hidden": 0,
"is_query_report": 1,
"label": "Delivery Note Trends",
"is_query_report": 0,
"label": "Loyalty Program",
"link_count": 0,
"link_to": "Delivery Note Trends",
"link_type": "Report",
"link_to": "Loyalty Program",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Sales Invoice",
"hidden": 0,
"is_query_report": 1,
"label": "Sales Invoice Trends",
"is_query_report": 0,
"label": "Loyalty Point Entry",
"link_count": 0,
"link_to": "Sales Invoice Trends",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Customer",
"hidden": 0,
"is_query_report": 1,
"label": "Customer Credit Balance",
"link_count": 0,
"link_to": "Customer Credit Balance",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Customer",
"hidden": 0,
"is_query_report": 1,
"label": "Customers Without Any Sales Transactions",
"link_count": 0,
"link_to": "Customers Without Any Sales Transactions",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Customer",
"hidden": 0,
"is_query_report": 1,
"label": "Sales Partners Commission",
"link_count": 0,
"link_to": "Sales Partners Commission",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Sales Order",
"hidden": 0,
"is_query_report": 1,
"label": "Territory Target Variance Based On Item Group",
"link_count": 0,
"link_to": "Territory Target Variance Based On Item Group",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Sales Order",
"hidden": 0,
"is_query_report": 1,
"label": "Sales Person Target Variance Based On Item Group",
"link_count": 0,
"link_to": "Sales Person Target Variance Based On Item Group",
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"dependencies": "Sales Order",
"hidden": 0,
"is_query_report": 1,
"label": "Sales Partner Target Variance Based On Item Group",
"link_count": 0,
"link_to": "Sales Partner Target Variance based on Item Group",
"link_type": "Report",
"link_to": "Loyalty Point Entry",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
@@ -458,7 +388,7 @@
"hidden": 0,
"is_query_report": 0,
"label": "Key Reports",
"link_count": 22,
"link_count": 9,
"onboard": 0,
"type": "Card Break"
},
@@ -562,15 +492,12 @@
"type": "Link"
},
{
"dependencies": "Lead",
"hidden": 0,
"is_query_report": 1,
"label": "Lead Details",
"link_count": 0,
"link_to": "Lead Details",
"link_type": "Report",
"is_query_report": 0,
"label": "Other Reports",
"link_count": 11,
"onboard": 0,
"type": "Link"
"type": "Card Break"
},
{
"dependencies": "Address",
@@ -692,29 +619,26 @@
"link_type": "Report",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 1,
"label": "Payment Terms Status for Sales Order",
"link_count": 0,
"link_to": "Payment Terms Status for Sales Order",
"link_type": "Report",
"onboard": 0,
"type": "Link"
}
],
"modified": "2023-04-16 13:29:55.087240",
"modified": "2023-05-26 16:31:53.634851",
"modified_by": "Administrator",
"module": "Selling",
"name": "Selling",
"number_cards": [],
"owner": "Administrator",
"parent_page": "",
"public": 1,
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
"sequence_id": 23.0,
"sequence_id": 6.0,
"shortcuts": [
{
"label": "Point of Sale",
"link_to": "point-of-sale",
"type": "Page"
},
{
"color": "Grey",
"format": "{} Available",
@@ -739,11 +663,6 @@
"stats_filter": "{ \"Status\": \"Open\" }",
"type": "Report"
},
{
"label": "Sales Order Analysis",
"link_to": "Sales Order Analysis",
"type": "Report"
},
{
"label": "Dashboard",
"link_to": "Selling",

View File

@@ -281,7 +281,9 @@ def get_employee_email(employee_doc):
def get_holiday_list_for_employee(employee, raise_exception=True):
if employee:
holiday_list, company = frappe.db.get_value("Employee", employee, ["holiday_list", "company"])
holiday_list, company = frappe.get_cached_value(
"Employee", employee, ["holiday_list", "company"]
)
else:
holiday_list = ""
company = frappe.db.get_single_value("Global Defaults", "default_company")

View File

@@ -115,6 +115,8 @@ def is_holiday(holiday_list, date=None):
if date is None:
date = today()
if holiday_list:
return bool(frappe.get_all("Holiday List", dict(name=holiday_list, holiday_date=date)))
return bool(
frappe.db.exists("Holiday", {"parent": holiday_list, "holiday_date": date}, cache=True)
)
else:
return False

View File

@@ -27,10 +27,12 @@ def after_install():
create_default_success_action()
create_default_energy_point_rules()
create_incoterms()
create_default_role_profiles()
add_company_to_session_defaults()
add_standard_navbar_items()
add_app_name()
setup_log_settings()
hide_workspaces()
frappe.db.commit()
@@ -205,3 +207,47 @@ def setup_log_settings():
log_settings.append("logs_to_clear", {"ref_doctype": "Repost Item Valuation", "days": 60})
log_settings.save(ignore_permissions=True)
def hide_workspaces():
for ws in ["Integration", "Settings"]:
frappe.db.set_value("Workspace", ws, "public", 0)
def create_default_role_profiles():
for role_profile_name, roles in DEFAULT_ROLE_PROFILES.items():
role_profile = frappe.new_doc("Role Profile")
role_profile.role_profile = role_profile_name
for role in roles:
role_profile.append("roles", {"role": role})
role_profile.insert(ignore_permissions=True)
DEFAULT_ROLE_PROFILES = {
"Inventory": [
"Stock User",
"Stock Manager",
"Item Manager",
],
"Manufacturing": [
"Stock User",
"Manufacturing User",
"Manufacturing Manager",
],
"Accounts": [
"Accounts User",
"Accounts Manager",
],
"Sales": [
"Sales User",
"Stock User",
"Sales Manager",
],
"Purchase": [
"Item Manager",
"Stock User",
"Purchase User",
"Purchase Manager",
],
}

View File

@@ -22,24 +22,15 @@
"creation": "2021-11-22 12:19:15.888642",
"docstatus": 0,
"doctype": "Module Onboarding",
"documentation_url": "https://docs.erpnext.com/docs/v13/user/manual/en/setting-up/company-setup",
"documentation_url": "https://docs.erpnext.com/docs/v14/user/manual/en/setting-up/company-setup",
"idx": 0,
"is_complete": 0,
"modified": "2022-06-07 14:31:00.575193",
"modified": "2023-05-23 13:20:19.703506",
"modified_by": "Administrator",
"module": "Setup",
"name": "Home",
"owner": "Administrator",
"steps": [
{
"step": "Company Set Up"
},
{
"step": "Navigation Help"
},
{
"step": "Data import"
},
{
"step": "Create an Item"
},
@@ -47,16 +38,10 @@
"step": "Create a Customer"
},
{
"step": "Create a Supplier"
},
{
"step": "Create a Quotation"
},
{
"step": "Letterhead"
"step": "Create Your First Sales Invoice"
}
],
"subtitle": "Company, Item, Customer, Supplier, Navigation Help, Data Import, Letter Head, Quotation",
"success_message": "Masters are all set up!",
"title": "Let's Set Up Some Masters"
"subtitle": "Item, Customer, Supplier and Quotation",
"success_message": "You're ready to start your journey with ERPNext",
"title": "Let's begin your journey with ERPNext"
}

View File

@@ -5,11 +5,11 @@
"description": "# Set Up a Company\n\nA company is a legal entity for which you will set up your books of account and create accounting transactions. In ERPNext, you can create multiple companies, and establish relationships (group/subsidiary) among them.\n\nWithin the company master, you can capture various default accounts for that Company and set crucial settings related to the accounting methodology followed for a company.\n",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"idx": 1,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2021-12-15 14:22:18.317423",
"modified": "2023-05-15 09:18:42.895537",
"modified_by": "Administrator",
"name": "Company Set Up",
"owner": "Administrator",

View File

@@ -5,17 +5,17 @@
"description": "# Create a Customer\n\nThe Customer master is at the heart of your sales transactions. Customers are linked in Quotations, Sales Orders, Invoices, and Payments. Customers can be either numbered or identified by name (you would typically do this based on the number of customers you have).\n\nThrough Customer\u2019s master, you can effectively track essentials like:\n - Customer\u2019s multiple address and contacts\n - Account Receivables\n - Credit Limit and Credit Period\n",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"idx": 1,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2021-12-15 14:20:31.197564",
"modified": "2023-05-23 12:45:55.138580",
"modified_by": "Administrator",
"name": "Create a Customer",
"owner": "Administrator",
"reference_document": "Customer",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Manage Customers",
"title": "Create a Customer",
"validate_action": 1
}

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