Compare commits

...

463 Commits

Author SHA1 Message Date
Frappe PR Bot
d1b2ce35b6 chore(release): Bumped to Version 14.36.0
# [14.36.0](https://github.com/frappe/erpnext/compare/v14.35.2...v14.36.0) (2023-08-23)

### Bug Fixes

* Accounts Payable Currency bug ([94612b9](94612b90ef))
* Add company filters for account ([e62ffa9](e62ffa990d))
* add missing items labels back ([#36737](https://github.com/frappe/erpnext/issues/36737)) ([3634e80](3634e80341))
* broken advance field in Accounts Receivable summary rpt ([296a4d7](296a4d7a12))
* broken consolidated report due to finance book filter ([5bd2a09](5bd2a0923f))
* check tax and charges if it is passed ([1f76c69](1f76c6972b))
* clear dimension defaults after test ([2a467a9](2a467a9fbf))
* dict value for dimension for gl entries defined without the dimension ([11ba553](11ba553dbd))
* dimension name in remark ([01ae513](01ae513f70))
* divide offsetting amount for multiple dimensions ([1269f2d](1269f2d301))
* divide offsetting amount only when account exists ([2c8c3a0](2c8c3a022c))
* Document Name link validation in Bank Reconciliation Tool ([#36495](https://github.com/frappe/erpnext/issues/36495)) ([83cbc1b](83cbc1bef6)), closes [#35540](https://github.com/frappe/erpnext/issues/35540) [#35540](https://github.com/frappe/erpnext/issues/35540)
* don't throw if item does not have default BOM (backport [#36709](https://github.com/frappe/erpnext/issues/36709)) ([#36734](https://github.com/frappe/erpnext/issues/36734)) ([e1bd9a7](e1bd9a7e8d))
* duplicate acc dimension in test ([cdb66bf](cdb66bf198))
* fetch acc dimensions correctly when fieldname is different from name ([7ac35b4](7ac35b496a))
* fetch accounting dimension details specific to company ([c1f1a21](c1f1a21714))
* include gain/loss journal in AR/AP reports ([4606079](4606079568))
* incorrect gl balance on multi company setup ([cb9aad3](cb9aad3e87))
* incorrect schedule in asset value adjustment ([#36725](https://github.com/frappe/erpnext/issues/36725)) ([a0575ed](a0575ed2b0))
* make offsetting entry for acc dimensions ([3198f26](3198f2669d))
* make offsetting entry for all doctypes ([f578c32](f578c3219d))
* mode of payment fetched from pos profile company in POS ([c74a414](c74a414313))
* not able to make stock entry ([#36759](https://github.com/frappe/erpnext/issues/36759)) ([873ee38](873ee384a1))
* Procurement Tracker report not showing material request items ([#36768](https://github.com/frappe/erpnext/issues/36768)) ([6a9935c](6a9935c00e))
* reset dimension defaults when company changedin test ([37ef6e9](37ef6e959b))
* **RFQ:** make "update password" and "submit quotation" buttons the same size (backport [#36667](https://github.com/frappe/erpnext/issues/36667)) ([#36686](https://github.com/frappe/erpnext/issues/36686)) ([36147ec](36147ec02c))
* timeout error coming during reposting ([#36715](https://github.com/frappe/erpnext/issues/36715)) ([620b21f](620b21fec5))
* **ux:** change batch selection message to alert (backport [#36500](https://github.com/frappe/erpnext/issues/36500)) ([#36697](https://github.com/frappe/erpnext/issues/36697)) ([0a4947a](0a4947a8f6))
* **UX:** Ignore prepared report ([3e23e1f](3e23e1fb91))

### Features

* **RFQ:** make email message fully configurable (backport [#36353](https://github.com/frappe/erpnext/issues/36353)) ([#36531](https://github.com/frappe/erpnext/issues/36531)) ([c308bd5](c308bd5309))
* Tick on checkbox to include draft timesheets (backport [#36577](https://github.com/frappe/erpnext/issues/36577)) ([#36640](https://github.com/frappe/erpnext/issues/36640)) ([9668615](9668615f7e))
* utility to repost accounting ledgers without cancellation ([#36469](https://github.com/frappe/erpnext/issues/36469)) ([f8d6fe6](f8d6fe6be0))

### Performance Improvements

* pull latest details only for referenced vouchers ([47345e8](47345e81a1))
2023-08-23 02:59:58 +00:00
Deepesh Garg
a0a3519cd3 Merge pull request #36763 from frappe/version-14-hotfix
chore: release v14
2023-08-23 08:28:36 +05:30
Deepesh Garg
f16386bd07 Merge pull request #36776 from frappe/mergify/bp/version-14-hotfix/pr-36758
fix: Accounts Payable Currency bug (#36758)
2023-08-22 22:37:39 +05:30
RitvikSardana
94612b90ef fix: Accounts Payable Currency bug
(cherry picked from commit 9349bc77c5)
2023-08-22 17:00:23 +00:00
rohitwaghchaure
6a9935c00e fix: Procurement Tracker report not showing material request items (#36768) 2023-08-22 19:25:50 +05:30
rohitwaghchaure
873ee384a1 fix: not able to make stock entry (#36759) 2023-08-22 16:57:08 +05:30
Deepesh Garg
13b1263723 Merge pull request #36738 from frappe/mergify/bp/version-14-hotfix/pr-36737
fix: add missing items label back (#36737)
2023-08-22 07:58:41 +05:30
ruthra kumar
6761874e28 Merge pull request #36749 from frappe/mergify/bp/version-14-hotfix/pr-36748
chore: clean up stale code in reconciliation tool (backport #36748)
2023-08-22 06:47:41 +05:30
ruthra kumar
19eb6af65f chore: clean up stale code in reconciliation tool
(cherry picked from commit e93b927051)
2023-08-22 00:50:48 +00:00
ruthra kumar
0dc0e287a3 Merge pull request #36741 from frappe/mergify/bp/version-14-hotfix/pr-36727
fix: broken advance field in Accounts Receivable summary rpt (backport #36727)
2023-08-22 06:05:54 +05:30
Anand Baburajan
a0575ed2b0 fix: incorrect schedule in asset value adjustment (#36725)
* fix: incorrect schedule in asset value adjustment

* chore: remove unnecessary commented code

* test: check schedules in test

* test: improving the test

* chore: better function name

* chore: use None instead of 0 for default value after depr

* chore: typo
2023-08-22 01:40:42 +05:30
ruthra kumar
cb9aad3e87 fix: incorrect gl balance on multi company setup 2023-08-21 21:35:26 +05:30
ruthra kumar
37cee42561 test: add test for receivable summary report
(cherry picked from commit af52f21ece)
2023-08-21 15:24:11 +00:00
ruthra kumar
928e475824 refactor: use payment ledger to fetch advance amount
(cherry picked from commit 0dc5e5c430)
2023-08-21 15:24:11 +00:00
ruthra kumar
296a4d7a12 fix: broken advance field in Accounts Receivable summary rpt
(cherry picked from commit 896b123fb1)
2023-08-21 15:24:10 +00:00
ruthra kumar
fe78076cde Merge pull request #36740 from frappe/mergify/bp/version-14-hotfix/pr-36728
fix: include gain/loss journal in AR/AP reports (backport #36728)
2023-08-21 20:20:35 +05:30
Deepesh Garg
d082e68e83 Merge pull request #36707 from frappe/mergify/bp/version-14-hotfix/pr-36149
fix: make offsetting entry for acc dimensions in general ledger (#36149)
2023-08-21 18:02:56 +05:30
ruthra kumar
4606079568 fix: include gain/loss journal in AR/AP reports
(cherry picked from commit e3104f1898)
2023-08-21 11:53:41 +00:00
Ankush Menat
3634e80341 fix: add missing items labels back (#36737)
[skip ci]

(cherry picked from commit 86cac1e1d2)
2023-08-21 10:37:24 +00:00
mergify[bot]
e1bd9a7e8d fix: don't throw if item does not have default BOM (backport #36709) (#36734)
* fix: don't throw if item does not have default BOM

(cherry picked from commit 268c19e745)

* fix: throw if `BOM No` is not set

(cherry picked from commit 2e22b019a0)

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-08-21 15:21:11 +05:30
Deepesh Garg
72d9dc6c85 chore: resolve more conflicts 2023-08-21 10:04:20 +05:30
Deepesh Garg
9ec11d9502 Merge pull request #36726 from frappe/mergify/bp/version-14-hotfix/pr-36696
fix: mode of payment fetched from pos profile company in POS (#36696)
2023-08-20 16:05:05 +05:30
Deepesh Garg
2f92981afe chore: resolve conflicts 2023-08-20 15:47:17 +05:30
Ritvik Sardana
c74a414313 fix: mode of payment fetched from pos profile company in POS
(cherry picked from commit 1bdd43d0f6)
2023-08-20 10:07:56 +00:00
ruthra kumar
e8f1c82089 Merge pull request #36672 from frappe/mergify/bp/version-14-hotfix/pr-36469
feat: utility to repost accounting ledgers without cancellation (backport #36469)
2023-08-19 19:54:55 +05:30
ruthra kumar
6366f5aadb Merge pull request #36679 from frappe/mergify/bp/version-14-hotfix/pr-36649
perf: pull latest details only for referenced vouchers (backport #36649)
2023-08-19 19:29:00 +05:30
Deepesh Garg
933d4bfceb Merge pull request #36711 from frappe/mergify/bp/version-14-hotfix/pr-36710
fix: broken consolidated report due to finance book filter (#36710)
2023-08-19 10:25:11 +05:30
Frappe PR Bot
de3b67c327 chore(release): Bumped to Version 14.35.2
## [14.35.2](https://github.com/frappe/erpnext/compare/v14.35.1...v14.35.2) (2023-08-18)

### Bug Fixes

* timeout error coming during reposting (backport [#36715](https://github.com/frappe/erpnext/issues/36715)) ([#36716](https://github.com/frappe/erpnext/issues/36716)) ([d5a596d](d5a596dff1))
2023-08-18 15:30:59 +00:00
mergify[bot]
d5a596dff1 fix: timeout error coming during reposting (backport #36715) (#36716)
fix: timeout error coming during reposting (#36715)

(cherry picked from commit 620b21fec5)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-08-18 20:59:20 +05:30
rohitwaghchaure
620b21fec5 fix: timeout error coming during reposting (#36715) 2023-08-18 18:33:04 +05:30
ruthra kumar
5bd2a0923f fix: broken consolidated report due to finance book filter
(cherry picked from commit 96847db0ec)
2023-08-18 09:25:18 +00:00
Deepesh Garg
e62ffa990d fix: Add company filters for account
(cherry picked from commit ecca9cb023)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
2023-08-18 07:08:21 +00:00
Gursheen Anand
01ae513f70 fix: dimension name in remark
(cherry picked from commit 4f9242d699)
2023-08-18 07:08:21 +00:00
Gursheen Anand
37ef6e959b fix: reset dimension defaults when company changedin test
(cherry picked from commit 3f5afb9cac)
2023-08-18 07:08:21 +00:00
Gursheen Anand
2a467a9fbf fix: clear dimension defaults after test
(cherry picked from commit 23e56d3ec1)
2023-08-18 07:08:20 +00:00
Gursheen Anand
7ac35b496a fix: fetch acc dimensions correctly when fieldname is different from name
(cherry picked from commit e19a6f5dcb)
2023-08-18 07:08:20 +00:00
Gursheen Anand
cdb66bf198 fix: duplicate acc dimension in test
(cherry picked from commit b3f6d991b5)
2023-08-18 07:08:20 +00:00
Gursheen Anand
8530a28c62 test: PI offsetting entry for accounting dimension
(cherry picked from commit 77deac4fb9)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
2023-08-18 07:08:20 +00:00
Gursheen Anand
2c8c3a022c fix: divide offsetting amount only when account exists
(cherry picked from commit 3a3ffa2307)
2023-08-18 07:08:19 +00:00
Gursheen Anand
1269f2d301 fix: divide offsetting amount for multiple dimensions
(cherry picked from commit 1e1e4b93c1)
2023-08-18 07:08:19 +00:00
Gursheen Anand
11ba553dbd fix: dict value for dimension for gl entries defined without the dimension
(cherry picked from commit ed3bef1840)
2023-08-18 07:08:19 +00:00
Gursheen Anand
248d4082c0 test: TB report balanced whenfiltered using acc dimension
(cherry picked from commit 4004427892)
2023-08-18 07:08:19 +00:00
Gursheen Anand
f578c3219d fix: make offsetting entry for all doctypes
(cherry picked from commit 22ba12172f)
2023-08-18 07:08:18 +00:00
Gursheen Anand
c1f1a21714 fix: fetch accounting dimension details specific to company
(cherry picked from commit 4e09de4db2)
2023-08-18 07:08:18 +00:00
Gursheen Anand
3198f2669d fix: make offsetting entry for acc dimensions
(cherry picked from commit d3759b3971)
2023-08-18 07:08:18 +00:00
Deepesh Garg
a623469778 Merge pull request #36645 from frappe/mergify/bp/version-14-hotfix/pr-36495
fix: Document Name link validation in Bank Reconciliation Tool (#36495)
2023-08-18 09:27:10 +05:30
Deepesh Garg
ee5a1d91ff chore: resolve conflicts 2023-08-17 20:45:53 +05:30
mergify[bot]
0a4947a8f6 fix(ux): change batch selection message to alert (backport #36500) (#36697)
fix(ux): change batch selection message to alert (#36500)

* fix(ux): change batch selection message to alert

* chore: linters

(cherry picked from commit 641fe7738c)

Co-authored-by: Dany Robert <danyrt@wahni.com>
2023-08-17 18:03:58 +05:30
Frappe PR Bot
2aa3b30b56 chore(release): Bumped to Version 14.35.1
## [14.35.1](https://github.com/frappe/erpnext/compare/v14.35.0...v14.35.1) (2023-08-17)

### Bug Fixes

* **ux:** change batch selection message to alert ([#36500](https://github.com/frappe/erpnext/issues/36500)) ([641fe77](641fe7738c))
2023-08-17 12:32:49 +00:00
Dany Robert
641fe7738c fix(ux): change batch selection message to alert (#36500)
* fix(ux): change batch selection message to alert

* chore: linters
2023-08-17 18:01:15 +05:30
mergify[bot]
9668615f7e feat: Tick on checkbox to include draft timesheets (backport #36577) (#36640)
feat: Tick on checkbox to include draft timesheets (#36577)

feat: Tick on Check box to include Draft Timesheets
(cherry picked from commit 75652799cd)

Co-authored-by: ViralKansodiya <141210323+viralkansodiya@users.noreply.github.com>
2023-08-17 17:13:51 +05:30
Deepesh Garg
506ac10119 Merge pull request #36678 from frappe/mergify/bp/version-14-hotfix/pr-36677
fix(UX): Ignore prepared report (#36677)
2023-08-17 16:08:36 +05:30
mergify[bot]
36147ec02c fix(RFQ): make "update password" and "submit quotation" buttons the same size (backport #36667) (#36686)
fix(RFQ): make "update password" and "submit quotation" buttons the same size (#36667)

fix(RFQ): button styling

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-08-17 16:00:17 +05:30
Shariq Ansari
3dd642441b Merge pull request #36689 from frappe/mergify/bp/version-14-hotfix/pr-36685
fix: check tax and charges if it is passed (backport #36685)
2023-08-17 13:10:42 +05:30
Shariq Ansari
f186015f58 chore: linter fix
(cherry picked from commit 21c1141fdb)
2023-08-17 07:40:10 +00:00
Shariq Ansari
1f76c6972b fix: check tax and charges if it is passed
(cherry picked from commit 7ec6909159)
2023-08-17 07:40:10 +00:00
mergify[bot]
c308bd5309 feat(RFQ): make email message fully configurable (backport #36353) (#36531)
* feat(RFQ): make email message fully configurable (#36353)

feat: make RFQ message fully configurable
(cherry picked from commit 21080afd92)

* fix(RFQ): hide description in print and report

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-08-17 12:11:37 +05:30
ruthra kumar
3f33d4cf76 chore: resolve conflicts 2023-08-17 11:07:59 +05:30
ruthra kumar
47345e81a1 perf: pull latest details only for referenced vouchers
(cherry picked from commit deb0d71294)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/payment_entry.py
2023-08-17 03:31:54 +00:00
Deepesh Garg
3e23e1fb91 fix(UX): Ignore prepared report
(cherry picked from commit 124c0dbd88)
2023-08-16 16:03:01 +00:00
ruthra kumar
b6134749c3 chore: resolve conflict 2023-08-16 16:44:07 +05:30
ruthra kumar
f8d6fe6be0 feat: utility to repost accounting ledgers without cancellation (#36469)
* feat: introduce doctypes for repost

* refactor: basic filters and validations

* chore: basic validations

* chore: added barebones function to generate ledger entries

* chore: repost on submit

* chore: repost in background

* chore: include payment entry and journal entry

* chore: ignore repost doc on cancel

* chore: preview method

* chore: rudimentary form of preview

* refactor: preview template

* refactor: basic background colors to differentiate old and new

* chore: remove commented code

* test: basic functionality

* chore: fix conflict

* chore: prevent repost on invoices with deferred accounting

* refactor(test): rename and test basic validations and methods

* refactor(test): test all validations

* fix(test): use proper name account name

* refactor(test): fix failing test case

* refactor(test): clear old entries

* refactor(test): simpler logic to clear old records

* refactor(test): make use of deletion flag

* refactor(test): split into multiple test cases

(cherry picked from commit e64b004eca)

# Conflicts:
#	erpnext/accounts/doctype/journal_entry/journal_entry.js
2023-08-16 11:04:45 +00:00
Frappe PR Bot
b773465b41 chore(release): Bumped to Version 14.35.0
# [14.35.0](https://github.com/frappe/erpnext/compare/v14.34.3...v14.35.0) (2023-08-16)

### Bug Fixes

* Allow backdated repayment cancels for term loans ([1377cf4](1377cf4cf1))
* allow negative stock condition for batch item ([#36586](https://github.com/frappe/erpnext/issues/36586)) ([ee04c6d](ee04c6d5c5))
* AR/AP report based on payment terms ([#36574](https://github.com/frappe/erpnext/issues/36574)) ([ac0fff7](ac0fff7e94))
* better remarks on Cr note created by Reconciliation ([5443592](5443592d84))
* Button Alignment center in hero slider ([#36607](https://github.com/frappe/erpnext/issues/36607)) ([b131f70](b131f70ed6)), closes [#36561](https://github.com/frappe/erpnext/issues/36561)
* cr/dr note should be posted for exc gain/loss ([349601b](349601b4b9))
* disallow mulitple SO with same PO No ([b901cfd](b901cfdbe2))
* don't show disabled items in `Item Shortage Report` (backport [#36550](https://github.com/frappe/erpnext/issues/36550)) ([#36571](https://github.com/frappe/erpnext/issues/36571)) ([19cfcea](19cfcea78e))
* fetch `Stock UOM` from Item if not set (backport [#36606](https://github.com/frappe/erpnext/issues/36606)) ([#36617](https://github.com/frappe/erpnext/issues/36617)) ([8b13185](8b13185c25))
* Group Account total not showing in Financial Statements ([2912648](2912648151))
* incorrect available qty for backdated stock reco with batch ([#36581](https://github.com/frappe/erpnext/issues/36581)) ([2800ad3](2800ad39d2))
* incorrect gain/loss on allocation change on reconciliation tool ([39c439d](39c439dc4b))
* Make default sales update frequency as monthly instead of each transaction ([4ca1f3b](4ca1f3b9cf))
* move company rename to long queue ([8083c0b](8083c0b59e))
* precision issue while submitting the stock entry ([#36575](https://github.com/frappe/erpnext/issues/36575)) ([a864e07](a864e07d4f))
* re-add permission that was unintentionally removed ([#36663](https://github.com/frappe/erpnext/issues/36663)) ([99777d3](99777d3fa4))
* **RFQ:** link to supplier portal ([eb2f68e](eb2f68ec98))
* standard formula to calculate the "difference" ([#36612](https://github.com/frappe/erpnext/issues/36612)) ([716d5c0](716d5c0b98))
* Tax withholding post LDC limit consumed ([#36611](https://github.com/frappe/erpnext/issues/36611)) ([1deebe8](1deebe8757))
* **test:** replace hardcoded reference to adv with dynamic one ([2e6bfa3](2e6bfa36de))
* **test:** test case breakage in Github Actions ([3542df7](3542df70f6))
* unhide `uom` and `stock_uom` fields in print view ([b49309c](b49309c160))
* validation blocks partial payment for SO and PO ([ce08f03](ce08f038d2))
* wrap none type rate under flt (backport [#36602](https://github.com/frappe/erpnext/issues/36602)) ([#36604](https://github.com/frappe/erpnext/issues/36604)) ([63c061e](63c061e5e8))
* wrong currency on financial-statement based reports ([#36524](https://github.com/frappe/erpnext/issues/36524)) ([3044f46](3044f46c52))

### Features

* add voucher totals in tds payable report ([#36568](https://github.com/frappe/erpnext/issues/36568)) ([90b390c](90b390c2c5))
* daily asset depreciation method ([#36587](https://github.com/frappe/erpnext/issues/36587)) ([2918127](29181274c8))
* Reallow customizing company abbreviation on setup. ([#36646](https://github.com/frappe/erpnext/issues/36646)) ([ca34b63](ca34b63470))

### Performance Improvements

* **invoice:** Faster return amount query (backport [#36556](https://github.com/frappe/erpnext/issues/36556)) ([#36557](https://github.com/frappe/erpnext/issues/36557)) ([fe41be9](fe41be953d))
2023-08-16 06:34:09 +00:00
Deepesh Garg
787118d00b Merge pull request #36658 from frappe/version-14-hotfix
chore: release v14
2023-08-16 12:02:35 +05:30
Deepesh Garg
67c8350f70 Merge branch 'version-14' into version-14-hotfix 2023-08-16 11:08:08 +05:30
ruthra kumar
cba1c63beb Merge pull request #36651 from frappe/mergify/bp/version-14-hotfix/pr-36642
refactor: toggle for negative item rates in Selling Settings (backport #36642)
2023-08-16 10:30:24 +05:30
ruthra kumar
2f0e7fcb4a Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-36642 2023-08-16 09:58:31 +05:30
ruthra kumar
44c1b91ca7 Merge pull request #36633 from frappe/mergify/bp/version-14-hotfix/pr-35644
refactor: booking exchange gain/loss amount through journal (backport #35644)
2023-08-16 09:41:25 +05:30
mergify[bot]
1deebe8757 fix: Tax withholding post LDC limit consumed (#36611)
* fix: Tax withholding post LDC limit consumed (#36611)

* fix: Tax withholding post LDC limit consumed

* fix: LDC condition check

(cherry picked from commit 985ff9781b)

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

* chore: resolve conflicts

* chore: linting issues

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-08-16 08:09:38 +05:30
mergify[bot]
99777d3fa4 fix: re-add permission that was unintentionally removed (#36663)
fix: re-add permission that was unintentionally removed

Remove `Reversal OF ITC` and re-add permissions. Both of them
unintended changes

(cherry picked from commit 45662fa646)

Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2023-08-16 08:09:04 +05:30
mergify[bot]
33d5250cec chore: add validation for depreciation expense account in asset category (backport #36659) (#36661)
chore: add validation for depreciation expense account in asset category (#36659)

(cherry picked from commit e0c79d3b53)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-08-15 18:27:01 +05:30
ruthra kumar
e55c160438 chore: resolve conflicts 2023-08-15 08:56:37 +05:30
ruthra kumar
3a82eb4ccf refactor: toggle for negative rates in Selling Settings
(cherry picked from commit a0fc68538f)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
#	erpnext/patches.txt
#	erpnext/selling/doctype/selling_settings/selling_settings.json
2023-08-15 02:43:54 +00:00
Devin Slauenwhite
ca34b63470 feat: Reallow customizing company abbreviation on setup. (#36646)
Co-authored-by: Bernd Oliver Sünderhauf <pancho@mailbox.org>
2023-08-14 21:44:03 +05:30
Kevin Shenk
83cbc1bef6 fix: Document Name link validation in Bank Reconciliation Tool (#36495)
fix: format_row broke Document Name link validation

#35540 broke Voucher Matching, leading to an invalid link exception on submission. This is because the format_row() function overwrites the row data instead of just providing a formatter on the DataTable column, and therefore passes through the formatted (linked) column data instead of the Document Name only.

This patch moves the appropriate frappe.form.formatters.Link function to a dedicated format hook on the DataTable columns definition, both fixing the error and retaining the functionality of #35540.

(cherry picked from commit 7ab55b1bb2)

# Conflicts:
#	erpnext/public/js/bank_reconciliation_tool/dialog_manager.js
2023-08-14 13:34:31 +00:00
mergify[bot]
716d5c0b98 fix: standard formula to calculate the "difference" (#36612)
fix: standard formula to calculate the "difference" (#36612)

(cherry picked from commit 843e77e72d)

Co-authored-by: HarryPaulo <paulo_fabris@hotmail.com>
2023-08-14 18:55:16 +05:30
Gursheen Kaur Anand
90b390c2c5 feat: add voucher totals in tds payable report (#36568)
* feat: voucher totals in tds payable monthly

* fix: naming series column in tds payable report

* fix: tds computation summary columns
2023-08-14 18:15:21 +05:30
ViralKansodiya
b131f70ed6 fix: Button Alignment center in hero slider (#36607)
fix: speling in CSS (Button alignment center is not working on hero slider)#36561
2023-08-14 16:20:58 +05:30
ruthra kumar
18cf93d1c8 refactor(test): import missing functions 2023-08-14 11:49:46 +05:30
ruthra kumar
2e6bfa36de fix(test): replace hardcoded reference to adv with dynamic one 2023-08-14 11:30:35 +05:30
ruthra kumar
b3f4c14a26 chore: resolve conflict in payment_reconciliation.py
backport will merge the better remarks PR
https://github.com/frappe/erpnext/pull/36573 wil exchange gain/loss
booking refactor
2023-08-14 10:56:48 +05:30
ruthra kumar
946aadb0c0 chore: resolve conflict in test_sales_invoice.py 2023-08-14 10:52:49 +05:30
ruthra kumar
f92453ae45 chore: resolve merge conflict in accounts/utils.py and its tests 2023-08-14 10:50:31 +05:30
ruthra kumar
7469018d3e chore: resolve merge conflict 2023-08-14 10:37:56 +05:30
ruthra kumar
61afffc908 chore: cancel gain/loss je while posting reverse gl
(cherry picked from commit 46ea814400)
2023-08-14 04:51:46 +00:00
ruthra kumar
ed0881dacb chore: don't make gain/loss journal for base currency transactions
(cherry picked from commit 567c0ce1e8)
2023-08-14 04:51:46 +00:00
ruthra kumar
efb293398a chore(test): use existing company for unit test
(cherry picked from commit 804afaa647)
2023-08-14 04:51:46 +00:00
ruthra kumar
8d32a1f4b3 chore: rename some internal variables
(cherry picked from commit d9d6856153)
2023-08-14 04:51:46 +00:00
ruthra kumar
4c527d6bba chore: add msgprint for exc JE
(cherry picked from commit acc7322874)
2023-08-14 04:51:45 +00:00
ruthra kumar
2a61d854d3 chore: use frappetestcase
(cherry picked from commit 47bbb37291)
2023-08-14 04:51:45 +00:00
ruthra kumar
052abcb075 refactor(test): assert ledger outstanding
(cherry picked from commit 025091161e)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
2023-08-14 04:51:45 +00:00
ruthra kumar
3542df70f6 fix(test): test case breakage in Github Actions
(cherry picked from commit bfa54d5335)
2023-08-14 04:51:45 +00:00
ruthra kumar
c5c440b7bc test: assert ledger after cr note cancellation
(cherry picked from commit ae424fdfed)
2023-08-14 04:51:45 +00:00
ruthra kumar
349601b4b9 fix: cr/dr note should be posted for exc gain/loss
(cherry picked from commit 95543225cf)
2023-08-14 04:51:44 +00:00
ruthra kumar
09e9b16b93 test: cr notes against invoice
(cherry picked from commit e3d2a2c5bd)
2023-08-14 04:51:44 +00:00
ruthra kumar
39c439dc4b fix: incorrect gain/loss on allocation change on reconciliation tool
(cherry picked from commit 506a5775f9)
2023-08-14 04:51:44 +00:00
ruthra kumar
72a507f888 refactor: create gain/loss on Cr/Dr notes with different exc rates
(cherry picked from commit ba1f065765)
2023-08-14 04:51:44 +00:00
ruthra kumar
22dbe52586 refactor: convert class method to standalone function
(cherry picked from commit 1ea1bfebc4)
2023-08-14 04:51:44 +00:00
ruthra kumar
1999132c28 refactor: split make_exchage_gain_loss_journal into smaller function
(cherry picked from commit c0b3b069b5)
2023-08-14 04:51:43 +00:00
ruthra kumar
57af6d9c21 refactor: cr/dr note will be on single exchange rate
(cherry picked from commit c87332d5da)

# Conflicts:
#	erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
2023-08-14 04:51:43 +00:00
ruthra kumar
a9faa92796 chore: type info
(cherry picked from commit 6628632fbb)
2023-08-14 04:51:43 +00:00
ruthra kumar
d9219dc7d2 chore(test): fix broken test case
(cherry picked from commit 37895a361c)
2023-08-14 04:51:43 +00:00
ruthra kumar
395f87a0f8 chore(test): fix broken unit test
(cherry picked from commit 70dd9d0671)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
2023-08-14 04:51:43 +00:00
ruthra kumar
4094fbc3e5 test: journals against sales invoice
(cherry picked from commit f3363e813a)
2023-08-14 04:51:42 +00:00
ruthra kumar
513721c338 refactor: handle diff amount in various names
(cherry picked from commit f4a65cccc4)
2023-08-14 04:51:42 +00:00
ruthra kumar
077d98e0fa refactor: unit tests for journals
(cherry picked from commit 5695d6a5a6)
2023-08-14 04:51:42 +00:00
ruthra kumar
8be312b73e refactor: dr/cr logic for journals as payments
(cherry picked from commit 0567243772)
2023-08-14 04:51:42 +00:00
ruthra kumar
197e5881aa refactor: assert payment ledger outstanding in both currencies
(cherry picked from commit 73cc1ba654)
2023-08-14 04:51:41 +00:00
ruthra kumar
7c3fc7eb3b refactor: cancel gain/loss JE on Journal as payment cancellation
(cherry picked from commit 6e18bb6456)
2023-08-14 04:51:41 +00:00
ruthra kumar
01953bc0e3 refactor: linkage between journal as payment and gain/loss journal
(cherry picked from commit f119a1e115)

# Conflicts:
#	erpnext/accounts/doctype/gl_entry/gl_entry.py
2023-08-14 04:51:41 +00:00
ruthra kumar
075a7dfe2e chore: code cleanup
(cherry picked from commit cd42b26839)
2023-08-14 04:51:41 +00:00
ruthra kumar
86aead3d45 refactor: remove call for setting deductions in payment entry
(cherry picked from commit 1bcb728c85)

# Conflicts:
#	erpnext/accounts/utils.py
2023-08-14 04:51:40 +00:00
ruthra kumar
e20b213737 refactor(test): difference amount no updated for exchange gain/loss
(cherry picked from commit 72bc5b3a11)

# Conflicts:
#	erpnext/accounts/test/test_utils.py
2023-08-14 04:51:40 +00:00
ruthra kumar
4025442491 refactor(test): exc gain/loss journal for advance in purchase invoice
(cherry picked from commit 5b06bd1af4)
2023-08-14 04:51:40 +00:00
ruthra kumar
f6bb6b78db refactor: only post on base currency for exchange gain/loss
(cherry picked from commit 78bc712756)
2023-08-14 04:51:40 +00:00
ruthra kumar
00a26ea6e6 refactor(test): payment will have same exch rate - no gain/loss
while making payment entry using reference to sales/purchase invoice,
it herits the parent docs exchange rate. so, there will be no exchange
gain/loss

(cherry picked from commit ee2d1fa36e)
2023-08-14 04:51:39 +00:00
ruthra kumar
e2c35f8c85 refactor(test): assert Exc journal when reconciling Journa to invoic
(cherry picked from commit 389cadf157)
2023-08-14 04:51:39 +00:00
ruthra kumar
72e88d22ed chore: remove debugging statements and fixing failing unit tests
(cherry picked from commit ee3ce82ea8)
2023-08-14 04:51:39 +00:00
ruthra kumar
220bf24555 refactor: exc booking logic for Journal Entry
(cherry picked from commit 7b516f8463)
2023-08-14 04:51:39 +00:00
ruthra kumar
1f76dde025 refactor(test): exc gain/loss booked through journal
(cherry picked from commit 00a2e42a47)
2023-08-14 04:51:38 +00:00
ruthra kumar
59b7e96255 refactor: assert exchange gain/loss amount in reference table
(cherry picked from commit 4ff53e1062)
2023-08-14 04:51:38 +00:00
ruthra kumar
d030f4a100 refactor: remove unused variable, pe should pull in parent exc rate
1. 'reference_doc' variable is never set. Hence, removing.
2. set_exchange_rate() relies on ref_doc, which was never
set due to point [1]. Replacing it with 'doc'.
3. Sales/Purchase Invoice has 'conversion_rate' field for tracking
exchange rate. Added a get statement for them as well.

(cherry picked from commit 92ae9c2201)
2023-08-14 04:51:38 +00:00
ruthra kumar
6c6acef78e refactor: helper method
(cherry picked from commit c1184585ed)
2023-08-14 04:51:38 +00:00
ruthra kumar
a9da619903 chore: fix logic for purchase invoice and some typos
(cherry picked from commit 34b5e849a2)
2023-08-14 04:51:38 +00:00
ruthra kumar
72005bdb39 refactor: add new reference type in journal entry account
(cherry picked from commit 13febcac81)
2023-08-14 04:51:38 +00:00
ruthra kumar
287af687cf chore: patch to update property setter for Journal Entry Accounts
(cherry picked from commit 0587338435)
2023-08-14 04:51:37 +00:00
ruthra kumar
db46987d4b refactor: replace with new method in purchase invoice
(cherry picked from commit 7e94a1c51b)
2023-08-14 04:51:37 +00:00
ruthra kumar
44110860b4 test: different scenarios for exchange booking
(cherry picked from commit 5e1cd1f227)
2023-08-14 04:51:37 +00:00
ruthra kumar
c06a6bfc09 refactor: book exchange gain/loss through journal
(cherry picked from commit 81cd7873d3)
2023-08-14 04:51:37 +00:00
mergify[bot]
ac0fff7e94 fix: AR/AP report based on payment terms (#36574)
fix: AR/AP report based on payment terms (#36574)

* fix: AR/AP report based on payment terms

* fix: AR/AP report based on payment terms

(cherry picked from commit fbb5058531)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-08-14 09:46:41 +05:30
mergify[bot]
3044f46c52 fix: wrong currency on financial-statement based reports (#36524)
fix: wrong currency on financial-statement based reports (#36524)

* add missing field options on financial_statement Total field

* format financial statement code

(cherry picked from commit ce25f9e8c9)

Co-authored-by: Naufal Afif <naufalafif58@gmail.com>
2023-08-13 17:11:47 +05:30
Deepesh Garg
0a832bc979 Merge pull request #36609 from frappe/mergify/bp/version-14-hotfix/pr-36564
fix: Make default sales update frequency as monthly instead of each transaction (#36564)
2023-08-13 13:20:27 +05:30
Deepesh Garg
e5bc888798 Merge pull request #36614 from deepeshgarg007/loan_repayment_cancel_v14
fix: Allow backdated repayment cancels for term loans
2023-08-13 11:57:49 +05:30
ruthra kumar
9172985a9b Merge pull request #36621 from frappe/mergify/bp/version-14-hotfix/pr-36309
fix: allocation validation blocks partial payment for SO and PO (backport #36309)
2023-08-13 11:39:27 +05:30
Deepesh Garg
a3032910a7 chore: resolve conflicts 2023-08-13 11:37:40 +05:30
ruthra kumar
ce08f038d2 fix: validation blocks partial payment for SO and PO
(cherry picked from commit cb2bfabb6f)
2023-08-13 10:58:55 +05:30
ruthra kumar
df632d7ecb Merge pull request #36620 from frappe/mergify/bp/version-14-hotfix/pr-36590
fix: disallow mulitple SO with same Purchase Order No if not enabled in Settings (backport #36590)
2023-08-13 08:48:27 +05:30
ruthra kumar
1693905703 Merge pull request #36619 from frappe/mergify/bp/version-14-hotfix/pr-36593
chore: update permissions for Process Payment Reconciliation (backport #36593)
2023-08-13 08:43:00 +05:30
ruthra kumar
21d3fb0625 chore: resolve merge conflict 2023-08-13 08:24:01 +05:30
ruthra kumar
c83f10f638 refactor(test): don't set po_no by default
(cherry picked from commit 64614cd915)

# Conflicts:
#	erpnext/stock/doctype/delivery_note/test_delivery_note.py
2023-08-13 02:40:42 +00:00
ruthra kumar
b901cfdbe2 fix: disallow mulitple SO with same PO No
(cherry picked from commit dbd3fdbb41)
2023-08-13 02:40:42 +00:00
ruthra kumar
cbcdf30840 chore: update permissions for Process Payment Reconciliation
(cherry picked from commit cd28d15292)
2023-08-13 02:40:38 +00:00
mergify[bot]
8b13185c25 fix: fetch Stock UOM from Item if not set (backport #36606) (#36617)
fix: fetch `Stock UOM` from Item if not set (#36606)

(cherry picked from commit 539cfd08f0)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-08-12 23:25:05 +05:30
Deepesh Garg
1377cf4cf1 fix: Allow backdated repayment cancels for term loans 2023-08-12 20:01:04 +05:30
Deepesh Garg
4ca1f3b9cf fix: Make default sales update frequency as monthly instead of each transaction
(cherry picked from commit 32863b4922)

# Conflicts:
#	erpnext/selling/doctype/selling_settings/selling_settings.json
2023-08-11 17:10:07 +00:00
Deepesh Garg
347c67a0f1 Merge pull request #36608 from frappe/mergify/bp/version-14-hotfix/pr-36582
fix: Group Account total not showing in Financial Statements (#36582)
2023-08-11 22:33:35 +05:30
Deepesh Garg
2912648151 fix: Group Account total not showing in Financial Statements
(cherry picked from commit baf5cddd1b)
2023-08-11 16:27:53 +00:00
Frappe PR Bot
ddcf555486 chore(release): Bumped to Version 14.34.3
## [14.34.3](https://github.com/frappe/erpnext/compare/v14.34.2...v14.34.3) (2023-08-11)

### Bug Fixes

* wrap none type rate under flt (backport [#36602](https://github.com/frappe/erpnext/issues/36602)) (backport [#36604](https://github.com/frappe/erpnext/issues/36604)) ([#36605](https://github.com/frappe/erpnext/issues/36605)) ([e4e1f03](e4e1f03bac))
2023-08-11 13:28:22 +00:00
mergify[bot]
e4e1f03bac fix: wrap none type rate under flt (backport #36602) (backport #36604) (#36605)
fix: wrap none type rate under flt (backport #36602) (#36604)

fix: wrap none type rate under flt (#36602)

(cherry picked from commit 627986efa1)

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-08-11 18:56:29 +05:30
mergify[bot]
63c061e5e8 fix: wrap none type rate under flt (backport #36602) (#36604)
fix: wrap none type rate under flt (#36602)

(cherry picked from commit 627986efa1)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-08-11 12:55:25 +00:00
mergify[bot]
96663d7e28 chore: set default filter dates if missing (backport #36597) (#36598)
chore: set default filter dates if missing (#36597)

(cherry picked from commit 98e82e0d99)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-08-11 09:19:14 +00:00
rohitwaghchaure
ee04c6d5c5 fix: allow negative stock condition for batch item (#36586)
* fix: allow negative stock condition for batch item

* fix: test case
2023-08-11 13:57:27 +05:30
ruthra kumar
a4d6e0092c Merge pull request #36594 from frappe/mergify/bp/version-14/pr-36593
chore: update permissions for Process Payment Reconciliation (backport #36593)
2023-08-11 12:14:48 +05:30
ruthra kumar
9821f90350 chore: update permissions for Process Payment Reconciliation
(cherry picked from commit cd28d15292)
2023-08-11 06:14:34 +00:00
ruthra kumar
5488785fd8 Merge pull request #36579 from frappe/mergify/bp/version-14-hotfix/pr-36573
refactor: 'is system generated' field and better remarks in Journal Entry (backport #36573)
2023-08-11 09:43:53 +05:30
ruthra kumar
25ac0ce1cc Merge pull request #36580 from frappe/mergify/bp/version-14-hotfix/pr-36578
fix: unhide `uom` and `stock_uom` fields in print view (backport #36578)
2023-08-11 09:16:07 +05:30
ruthra kumar
727a379581 chore: remove unwanted 'Reversal of ITC' and merge conflicts 2023-08-11 09:12:11 +05:30
Anand Baburajan
29181274c8 feat: daily asset depreciation method (#36587)
* feat: daily asset depreciation method

* chore: hide depr schedule if no schedules
2023-08-10 22:47:16 +05:30
Frappe PR Bot
35450d7bd9 chore(release): Bumped to Version 14.34.2
## [14.34.2](https://github.com/frappe/erpnext/compare/v14.34.1...v14.34.2) (2023-08-10)

### Bug Fixes

* incorrect available qty for backdated stock reco with batch (backport [#36581](https://github.com/frappe/erpnext/issues/36581)) ([#36585](https://github.com/frappe/erpnext/issues/36585)) ([bb112ec](bb112eca05))
2023-08-10 12:28:54 +00:00
mergify[bot]
bb112eca05 fix: incorrect available qty for backdated stock reco with batch (backport #36581) (#36585)
fix: incorrect available qty for backdated stock reco with batch (#36581)

(cherry picked from commit 2800ad39d2)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-08-10 17:57:17 +05:30
rohitwaghchaure
2800ad39d2 fix: incorrect available qty for backdated stock reco with batch (#36581) 2023-08-10 17:22:14 +05:30
ruthra kumar
b49309c160 fix: unhide uom and stock_uom fields in print view
(cherry picked from commit 11cd163db7)
2023-08-10 10:18:36 +00:00
ruthra kumar
a04471024d refactor: enable 'no-copy'
(cherry picked from commit 4ed4b0240d)
2023-08-10 10:09:26 +00:00
ruthra kumar
9b0d30c56b refactor: set flag display condition
(cherry picked from commit de17eaef38)
2023-08-10 10:09:25 +00:00
ruthra kumar
00c7dbceaa refactor: add is_system_generated field to Journal Entry
(cherry picked from commit 3997aa77d4)

# Conflicts:
#	erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
2023-08-10 10:09:25 +00:00
ruthra kumar
5443592d84 fix: better remarks on Cr note created by Reconciliation
(cherry picked from commit 47cb349362)

# Conflicts:
#	erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
2023-08-10 10:09:25 +00:00
Frappe PR Bot
10e25972e1 chore(release): Bumped to Version 14.34.1
## [14.34.1](https://github.com/frappe/erpnext/compare/v14.34.0...v14.34.1) (2023-08-10)

### Bug Fixes

* precision issue while submitting the stock entry (backport [#36575](https://github.com/frappe/erpnext/issues/36575)) ([#36576](https://github.com/frappe/erpnext/issues/36576)) ([d6ebd1b](d6ebd1b6f8))
2023-08-10 08:06:21 +00:00
mergify[bot]
d6ebd1b6f8 fix: precision issue while submitting the stock entry (backport #36575) (#36576)
fix: precision issue while submitting the stock entry (#36575)

fix: precision issue while submmiting the stock entry
(cherry picked from commit a864e07d4f)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-08-10 13:34:36 +05:30
rohitwaghchaure
a864e07d4f fix: precision issue while submitting the stock entry (#36575)
fix: precision issue while submmiting the stock entry
2023-08-10 13:32:31 +05:30
mergify[bot]
19cfcea78e fix: don't show disabled items in Item Shortage Report (backport #36550) (#36571)
fix: don't show disabled items in `Item Shortage Report` (#36550)

(cherry picked from commit 4a7fc1506f)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-08-10 11:29:20 +05:30
Ankush Menat
8083c0b59e fix: move company rename to long queue
(cherry picked from commit 5169006085)
2023-08-10 10:03:22 +05:30
Anand Baburajan
8abc0adb18 chore: add warning for lending separation (#36569) 2023-08-09 15:07:40 +00:00
mergify[bot]
fe41be953d perf(invoice): Faster return amount query (backport #36556) (#36557)
perf(invoice): Faster return amount query (#36556)

perf: Faster return amount query
(cherry picked from commit b0c79a0467)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-08-09 14:07:48 +05:30
s-aga-r
c2b5417cb8 Merge pull request #36552 from frappe/mergify/bp/version-14-hotfix/pr-36551
fix(RFQ): link to supplier portal (backport #36551)
2023-08-09 13:55:28 +05:30
Frappe PR Bot
259f3422d5 chore(release): Bumped to Version 14.34.0
# [14.34.0](https://github.com/frappe/erpnext/compare/v14.33.2...v14.34.0) (2023-08-09)

### Bug Fixes

* **accounts:** Translate columns in AP/AR report ([#36503](https://github.com/frappe/erpnext/issues/36503)) ([6739369](67393694de))
* AP and AR summary ([769d7d7](769d7d7554))
* check root type only when not none ([46bb309](46bb309b8a))
* cross connect delivery note and sales invoice ([#36183](https://github.com/frappe/erpnext/issues/36183)) ([8501a11](8501a1182a))
* Debit credit difference while submitting Sales Invoice ([#36523](https://github.com/frappe/erpnext/issues/36523)) ([240d866](240d866ef4))
* don't allow negative rate (backport [#36027](https://github.com/frappe/erpnext/issues/36027)) ([#36465](https://github.com/frappe/erpnext/issues/36465)) ([caa4f33](caa4f33169))
* enqueue submit/cancel action for stock entry having more than 50 line items (backport [#36532](https://github.com/frappe/erpnext/issues/36532)) ([#36536](https://github.com/frappe/erpnext/issues/36536)) ([9c108a8](9c108a8ef7))
* fetch ple for all party types ([674dba8](674dba8cd7))
* fetch ple with party type employee in AP ([1ca9aca](1ca9aca0d5))
* Fix query for financial statement report ([d1590f2](d1590f266b))
* get incoming rate instead of BOM rate (backport [#36496](https://github.com/frappe/erpnext/issues/36496)) ([#36506](https://github.com/frappe/erpnext/issues/36506)) ([bdfbccd](bdfbccd38e))
* handle None value in payment_term_outstanding ([b033b3b](b033b3b0d6))
* Lower deduction certificate for multi-company ([#36491](https://github.com/frappe/erpnext/issues/36491)) ([2216875](2216875bd6))
* payment allocation in invoice payment schedule ([#36440](https://github.com/frappe/erpnext/issues/36440)) ([0e87c86](0e87c86aab))
* search not working for so in the Production Plan ([#36459](https://github.com/frappe/erpnext/issues/36459)) ([8c57d56](8c57d56240))
* serial no not able to reject for the internal transfer ([#36467](https://github.com/frappe/erpnext/issues/36467)) ([c1819a4](c1819a4b21))
* stock entry decimal issue (backport [#36530](https://github.com/frappe/erpnext/issues/36530)) ([#36533](https://github.com/frappe/erpnext/issues/36533)) ([5b04708](5b04708164))
* stock reconciliation negative stock error (backport [#36544](https://github.com/frappe/erpnext/issues/36544)) ([#36549](https://github.com/frappe/erpnext/issues/36549)) ([00b9df0](00b9df0bc5))
* Tax withholding against order via Payment Entry ([#36493](https://github.com/frappe/erpnext/issues/36493)) ([a234b89](a234b8932e))
* use correct lang separator for frappe (backport [#36519](https://github.com/frappe/erpnext/issues/36519)) ([#36520](https://github.com/frappe/erpnext/issues/36520)) ([f9981d1](f9981d1ff3))
* **ux:** add `Ordered Qty` column in Get Items From > MR (backport [#36486](https://github.com/frappe/erpnext/issues/36486)) ([#36505](https://github.com/frappe/erpnext/issues/36505)) ([0d7a4b6](0d7a4b6ff6))

### Features

* ledger comparison report ([#36485](https://github.com/frappe/erpnext/issues/36485)) ([07f235c](07f235cf7d))
* **RFQ:** make sending attachments configurable (backport [#36359](https://github.com/frappe/erpnext/issues/36359)) ([#36535](https://github.com/frappe/erpnext/issues/36535)) ([5881960](5881960001))

### Performance Improvements

* asset depreciation entry posting ([#36461](https://github.com/frappe/erpnext/issues/36461)) ([cd1c175](cd1c175439))
* defer holiday list imports ([7adad42](7adad4272a))
2023-08-09 03:06:43 +00:00
Deepesh Garg
4f0bb5e643 Merge pull request #36546 from frappe/version-14-hotfix
chore: release v14
2023-08-09 08:35:06 +05:30
mergify[bot]
240d866ef4 fix: Debit credit difference while submitting Sales Invoice (#36523)
* fix: Debit credit difference while submitting Sales Invoice (#36523)

* fix: Debit credit difference while submitting Sales Invoice

* test(fix): Update gl entry comparison

* test(fix): Update gl entry comparison

(cherry picked from commit 492ea3bcc8)

# Conflicts:
#	erpnext/controllers/accounts_controller.py

* chore: resolve conflicts

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-08-09 00:04:47 +05:30
mergify[bot]
0e87c86aab fix: payment allocation in invoice payment schedule (#36440)
* fix: payment allocation in invoice payment schedule (#36440)

* fix: payment allocation in invoice payment schedule

* test: payment allocation for payment terms

* chore: linting issues

(cherry picked from commit edbefee10c)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py

* chore: resolve conflicts

---------

Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-08-09 00:04:08 +05:30
barredterra
d273948d7a test(RFQ): get_link
(cherry picked from commit 68ad62f7d0)
2023-08-08 11:25:39 +00:00
barredterra
eb2f68ec98 fix(RFQ): link to supplier portal
(cherry picked from commit fd91f2c2e0)
2023-08-08 11:25:39 +00:00
mergify[bot]
00b9df0bc5 fix: stock reconciliation negative stock error (backport #36544) (#36549)
fix: stock reconciliation negative stock error (#36544)

fix: stock reco negative stock error
(cherry picked from commit 0b36e7d10e)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-08-08 16:04:52 +05:30
Anand Baburajan
cd1c175439 perf: asset depreciation entry posting (#36461)
* perf: make post depr entries job daily_long

* perf: optimise post_depreciation_entries and make_depreciation_entry

* chore: more optimisation and dont fail all schedule dates if one date fails

* chore: don't post entries before acc_frozen_upto

* chore: using get_single_value

* refactor: destructure asset object
2023-08-08 15:09:55 +05:30
ruthra kumar
2ca2e67812 Merge pull request #36543 from frappe/mergify/bp/version-14-hotfix/pr-36528
refactor: use base_tax_withholding_net_total for treshold validation (backport #36528)
2023-08-08 14:41:53 +05:30
ruthra kumar
c26a52d791 refactor: use base_tax_withholding_net_total for treshold validation (#36528)
* refactor: use base_tax_withholding_net_total for treshold validation

* fix: only for non payment entry doctypes

(cherry picked from commit 11d5327d1b)
2023-08-08 08:45:22 +00:00
mergify[bot]
5881960001 feat(RFQ): make sending attachments configurable (backport #36359) (#36535)
* feat(RFQ): make sending attachments configurable (#36359)

(cherry picked from commit 8cc3df7c2c)

# Conflicts:
#	erpnext/buying/doctype/request_for_quotation/request_for_quotation.json

* chore: resolve conflicts

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-08-07 21:17:12 +05:30
mergify[bot]
9c108a8ef7 fix: enqueue submit/cancel action for stock entry having more than 50 line items (backport #36532) (#36536)
fix: enqueue submit/cancel action for stock entry having more than 50 line items (#36532)

(cherry picked from commit ecba6ee183)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-08-07 21:03:36 +05:30
mergify[bot]
5b04708164 fix: stock entry decimal issue (backport #36530) (#36533)
fix: stock entry decimal issue (#36530)

(cherry picked from commit 28dfc88789)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-08-07 19:11:19 +05:30
ruthra kumar
d76c2c5738 Merge pull request #36521 from frappe/mergify/bp/version-14-hotfix/pr-36485
feat: ledger comparison report (backport #36485)
2023-08-07 11:52:28 +05:30
ruthra kumar
07f235cf7d feat: ledger comparison report (#36485)
* feat: Accounting Ledger comparison report

* chore: barebones methods

* chore: working state

* chore: refactor internal logic

* chore: working multi select filter on Account

* chore: working voucher no filter

* chore: remove debugging statements

* chore: report with currency symbol

* chore: working start and end date filter

* test: basic report function

* refactor(test): test all filters

(cherry picked from commit b86747c9d4)
2023-08-07 06:00:24 +00:00
Ankush Menat
7adad4272a perf: defer holiday list imports
Only used for configuring but loaded whenever
get_doc("holiday list", ...) is done

(cherry picked from commit 2eea90a873)
2023-08-07 10:09:49 +05:30
mergify[bot]
f9981d1ff3 fix: use correct lang separator for frappe (backport #36519) (#36520)
* fix: use correct lang separator for frappe

(cherry picked from commit 0218ca538f)

* perf: defer babel import

Only required when configuring but will get loaded everywhere

(cherry picked from commit f574ac11ea)

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-08-07 10:08:59 +05:30
mergify[bot]
8770aa5955 chore: don't merge asset capitalization gl entries (backport #36514) (#36516)
chore: don't merge asset capitalization gl entries (#36514)

(cherry picked from commit 2d7d86039a)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-08-07 00:20:53 +05:30
Anand Baburajan
2d7d86039a chore: don't merge asset capitalization gl entries (#36514) 2023-08-06 23:34:02 +05:30
mergify[bot]
bdfbccd38e fix: get incoming rate instead of BOM rate (backport #36496) (#36506)
* fix: get incoming rate instead of BOM rate (#36496)

* fix: get incoming rate instead of BOM rate

* test: add test case for SCR rm rate

(cherry picked from commit 758b31d895)

# Conflicts:
#	erpnext/controllers/subcontracting_controller.py

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-08-06 14:51:23 +05:30
mergify[bot]
a234b8932e fix: Tax withholding against order via Payment Entry (#36493)
fix: Tax withholding against order via Payment Entry (#36493)

* fix: Tax withholding against order via Payment Entry

* test: Add test case

* fix: Nonetype exceptions

(cherry picked from commit 93767eb7fc)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-08-05 22:58:55 +05:30
mergify[bot]
2216875bd6 fix: Lower deduction certificate for multi-company (#36491)
fix: Lower deduction certificate for multi-company (#36491)

(cherry picked from commit 96035b87d5)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-08-05 22:44:10 +05:30
mergify[bot]
67393694de fix(accounts): Translate columns in AP/AR report (#36503)
fix(accounts): Translate columns in AP/AR report (#36503)

(cherry picked from commit 559d914c0b)

Co-authored-by: Corentin Flr <10946971+cogk@users.noreply.github.com>
2023-08-05 22:43:44 +05:30
mergify[bot]
0d7a4b6ff6 fix(ux): add Ordered Qty column in Get Items From > MR (backport #36486) (#36505)
fix(ux): add `Ordered Qty` column in Get Items From > MR (#36486)

(cherry picked from commit e179499764)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-08-05 19:31:55 +05:30
Deepesh Garg
e0fb398be1 Merge pull request #36488 from frappe/mergify/bp/version-14-hotfix/pr-36333
fix: AP report does not show expense claim payables (#36333)
2023-08-04 17:52:52 +05:30
mergify[bot]
cf2a3e2fc5 Contact Doctype don't have any field job_title. (#36156)
fix: Contact Doctype doesn't have any field called `job_title`

fix: Contact Doctype doesn't have any field called `job_title`
(cherry picked from commit 49be740736)

Co-authored-by: Sumit Jain <59503001+sumitjain236@users.noreply.github.com>
2023-08-04 17:52:29 +05:30
Gursheen Anand
a79b30e45f refactor: future payments query
(cherry picked from commit f5761e7965)
2023-08-04 12:14:04 +00:00
Gursheen Anand
769d7d7554 fix: AP and AR summary
(cherry picked from commit e355dea4b5)
2023-08-04 12:14:03 +00:00
Gursheen Anand
674dba8cd7 fix: fetch ple for all party types
(cherry picked from commit fd5c4e0a64)
2023-08-04 12:14:03 +00:00
Gursheen Anand
1ca9aca0d5 fix: fetch ple with party type employee in AP
(cherry picked from commit c47a37c3ab)
2023-08-04 12:14:02 +00:00
Deepesh Garg
8e8e59cecb Merge pull request #36484 from frappe/mergify/bp/version-14-hotfix/pr-36458
test: balance sheet report  (backport #36458)
2023-08-04 10:59:11 +05:30
Gursheen Anand
47d0e76999 test: balance sheet report
(cherry picked from commit 002bf77314)
2023-08-04 04:26:17 +00:00
Gursheen Anand
46bb309b8a fix: check root type only when not none
(cherry picked from commit cd98be6088)
2023-08-04 04:26:17 +00:00
mergify[bot]
dfd356a174 chore: better cost center validation for assets (backport #36477) (#36479)
chore: better cost center validation for assets (#36477)

(cherry picked from commit 38a612c62e)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-08-03 17:18:56 +05:30
Frappe PR Bot
2d43ba0c90 chore(release): Bumped to Version 14.33.2
## [14.33.2](https://github.com/frappe/erpnext/compare/v14.33.1...v14.33.2) (2023-08-03)

### Bug Fixes

* serial no not able to reject for the internal transfer (backport [#36467](https://github.com/frappe/erpnext/issues/36467)) ([#36475](https://github.com/frappe/erpnext/issues/36475)) ([a09777c](a09777c406))
2023-08-03 07:48:49 +00:00
mergify[bot]
a09777c406 fix: serial no not able to reject for the internal transfer (backport #36467) (#36475)
fix: serial no not able to reject for the internal transfer (#36467)

(cherry picked from commit c1819a4b21)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-08-03 13:16:55 +05:30
rohitwaghchaure
c1819a4b21 fix: serial no not able to reject for the internal transfer (#36467) 2023-08-03 12:19:25 +05:30
mergify[bot]
caa4f33169 fix: don't allow negative rate (backport #36027) (#36465)
* fix: don't allow negative rates (#36027)

* fix: don't allow negative rate

* test: don't allow negative rate

* fix: only check for -rate on items child table

(cherry picked from commit dedf24b86d)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py

* chore: resolve merge conflict

---------

Co-authored-by: Devin Slauenwhite <devin.slauenwhite@gmail.com>
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2023-08-02 17:13:22 +05:30
ruthra kumar
e6dca0668c Merge pull request #36464 from frappe/mergify/bp/version-14-hotfix/pr-36455
fix: handle None value in payment_term_outstanding (backport #36455)
2023-08-02 16:54:38 +05:30
Husam Hammad
b033b3b0d6 fix: handle None value in payment_term_outstanding
* Fix payment entry bug: Handle None value in payment_term_outstanding

* fix: Handle None value in payment_term_outstanding V2

fix linting issue

(cherry picked from commit 27ebf14f9d)
2023-08-02 10:59:43 +00:00
Frappe PR Bot
75012c17d4 chore(release): Bumped to Version 14.33.1
## [14.33.1](https://github.com/frappe/erpnext/compare/v14.33.0...v14.33.1) (2023-08-02)

### Bug Fixes

* search not working for so in the Production Plan (backport [#36459](https://github.com/frappe/erpnext/issues/36459)) ([#36460](https://github.com/frappe/erpnext/issues/36460)) ([4605bc5](4605bc51ae))
2023-08-02 07:31:21 +00:00
mergify[bot]
4605bc51ae fix: search not working for so in the Production Plan (backport #36459) (#36460)
fix: search not working for so in the Production Plan (#36459)

fix: search not working for so
(cherry picked from commit 8c57d56240)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-08-02 12:59:31 +05:30
rohitwaghchaure
8c57d56240 fix: search not working for so in the Production Plan (#36459)
fix: search not working for so
2023-08-02 12:47:12 +05:30
Anand Baburajan
8501a1182a fix: cross connect delivery note and sales invoice (#36183)
* fix: cross connect delivery note and sales invoice

* chore: remove unnecessary non_standard_fieldname
2023-08-02 09:07:04 +05:30
Deepesh Garg
fb32120e36 Merge pull request #36454 from frappe/mergify/bp/version-14-hotfix/pr-36450
fix: Fix query for financial statement report (backport #36450)
2023-08-01 23:39:44 +05:30
Corentin Flr
d1590f266b fix: Fix query for financial statement report
(cherry picked from commit bd3fc7c434)
2023-08-01 18:07:16 +00:00
Frappe PR Bot
710ec18086 chore(release): Bumped to Version 14.33.0
# [14.33.0](https://github.com/frappe/erpnext/compare/v14.32.1...v14.33.0) (2023-08-01)

### Bug Fixes

* allow fully depreciated existing assets ([#36378](https://github.com/frappe/erpnext/issues/36378)) ([43b85c5](43b85c5711))
* change fieldtype from Currency to Float for the valuation rate in the stock report ([93bd4c7](93bd4c7ff3))
* cost center filter for fetching payments ([c9daa85](c9daa85985))
* Defined "Open" Status as default (backport [#36421](https://github.com/frappe/erpnext/issues/36421)) ([#36424](https://github.com/frappe/erpnext/issues/36424)) ([33a9477](33a947726d))
* filter by cost center in trial balance ([02428b4](02428b446d))
* german translations ([9c8e2a3](9c8e2a33e9))
* GL Entries should not be splitted based on cost center allocation in PCV ([ade13e6](ade13e6d36))
* group item reorder by (warehouse, material_request_type) (backport [#35818](https://github.com/frappe/erpnext/issues/35818)) ([#36425](https://github.com/frappe/erpnext/issues/36425)) ([516191b](516191bf2b))
* Ignore account closing balance for financial statement ([800417e](800417eeed))
* in payment_entry 'Unallocated Amount' cal is broken ([#36369](https://github.com/frappe/erpnext/issues/36369)) ([cd3b85c](cd3b85ccff))
* incorrect `idx` on JE's after reconciliation ([80eb875](80eb8754db))
* incorrect qty set in the serial no picker ([57b19a5](57b19a523e))
* incorrect usage `get_cached_value` on single doctypes ([8f72a68](8f72a6814b))
* **Item Group:** allow root deletion ([1a6be5e](1a6be5e19b))
* job card suggest holiday as start date (backport [#35958](https://github.com/frappe/erpnext/issues/35958)) ([#36423](https://github.com/frappe/erpnext/issues/36423)) ([29ddd26](29ddd26ba1))
* Job Card validation fixed when displaying total completed quantity ([7b3bcd3](7b3bcd3bc4))
* multiple issues related to Production Plan (backport [#36377](https://github.com/frappe/erpnext/issues/36377)) ([#36381](https://github.com/frappe/erpnext/issues/36381)) ([a799e1b](a799e1b217))
* not able to make material request (backport [#36416](https://github.com/frappe/erpnext/issues/36416)) ([#36426](https://github.com/frappe/erpnext/issues/36426)) ([99e7406](99e7406b5b))
* only publish repost progress to doc subscriber (backport [#36400](https://github.com/frappe/erpnext/issues/36400)) ([#36402](https://github.com/frappe/erpnext/issues/36402)) ([32bdb7c](32bdb7cccd))
* overallocation validation misfire on normal invoices ([#36349](https://github.com/frappe/erpnext/issues/36349)) ([09af485](09af485d54))
* paid_amount when the group is mode of payment ([50ef358](50ef35845a))
* process_owner is not link User (backport [#36420](https://github.com/frappe/erpnext/issues/36420)) ([#36422](https://github.com/frappe/erpnext/issues/36422)) ([289dc36](289dc36696))
* removed "fetch_from" (backport [#36365](https://github.com/frappe/erpnext/issues/36365)) ([#36386](https://github.com/frappe/erpnext/issues/36386)) ([6d051f5](6d051f5732))
* root type in account map for balance sheet ([#36303](https://github.com/frappe/erpnext/issues/36303)) ([5b56296](5b562961e3))
* show invoices name instead of object address ([e802f0c](e802f0c352))
* show tds & tcs separately ([619b0fe](619b0feb5f))
* test with None conditon in PE ([479cab0](479cab0336))
* typo in loyalty program throw message ([#36432](https://github.com/frappe/erpnext/issues/36432)) ([782a4d1](782a4d1fa8))

### Features

* add party type filter ([32fea64](32fea643cd))

### Performance Improvements

* avoid full table scan in sle count check ([#36428](https://github.com/frappe/erpnext/issues/36428)) ([04f9915](04f9915009))
* move project status reminder to hourly (backport [#36372](https://github.com/frappe/erpnext/issues/36372)) ([#36373](https://github.com/frappe/erpnext/issues/36373)) ([4ac60cd](4ac60cd73b))
* use `LEFT JOIN` instead of `NOT EXISTS` (backport [#36221](https://github.com/frappe/erpnext/issues/36221)) ([#36383](https://github.com/frappe/erpnext/issues/36383)) ([26a0b3b](26a0b3b380))
2023-08-01 18:06:33 +00:00
Deepesh Garg
a9dcf46882 Merge pull request #36442 from frappe/version-14-hotfix
chore: release v14
2023-08-01 23:34:31 +05:30
ruthra kumar
e8f24b8b4e Merge pull request #36437 from frappe/mergify/bp/version-14-hotfix/pr-36349
fix: overallocation validation misfire on normal invoices (backport #36349)
2023-08-01 15:55:42 +05:30
ruthra kumar
fcda6818ed Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-36349 2023-08-01 15:08:06 +05:30
ruthra kumar
f748eb74f1 Merge pull request #36367 from frappe/mergify/bp/version-14-hotfix/pr-36126
fix: incorrect `idx` on Journals after reconciliation (backport #36126)
2023-08-01 15:07:23 +05:30
ruthra kumar
5f677b9629 Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-36126 2023-08-01 14:38:52 +05:30
ruthra kumar
d34f1c1776 Merge pull request #36439 from ruthra-kumar/merge_conflict_in_sr_sp_csv
chore: remove merge conflict
2023-08-01 14:31:11 +05:30
ruthra kumar
2dee97aa70 chore: remove merge conflict
remove merge conflict in sr-SP.csv
2023-08-01 14:18:14 +05:30
ruthra kumar
5ac7dca2ec Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-36126 2023-08-01 13:53:39 +05:30
ruthra kumar
ee02cde58d Merge pull request #36438 from ruthra-kumar/remove_merge_conflict_in_v14_hotfix
chore: remove merge conflict in translation file 'tr.csv'
2023-08-01 13:52:02 +05:30
ruthra kumar
14dc40360d chore: remove merge conflict in translation file
Remove merge conflict in v14-hotfix branch
2023-08-01 13:48:03 +05:30
ruthra kumar
09af485d54 fix: overallocation validation misfire on normal invoices (#36349)
* fix: overallocation validation misfire on normal invoices

* test: assert misfire doesn't happen

(cherry picked from commit ab933df5bb)
2023-08-01 07:43:04 +00:00
ruthra kumar
80eb8754db fix: incorrect idx on JE's after reconciliation
(cherry picked from commit 72f577aad2)
2023-08-01 11:49:27 +05:30
Anand Baburajan
43b85c5711 fix: allow fully depreciated existing assets (#36378) 2023-08-01 11:47:48 +05:30
mergify[bot]
04f9915009 perf: avoid full table scan in sle count check (#36428)
perf: avoid full table scan in sle count check (#36428)

(cherry picked from commit f31d07554d)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-08-01 11:20:26 +05:30
mergify[bot]
782a4d1fa8 fix: typo in loyalty program throw message (#36432)
* fix: typo in loyalty program throw message (#36432)

(cherry picked from commit 4f473eb090)

# Conflicts:
#	erpnext/translations/sr-SP.csv
#	erpnext/translations/tr.csv
#	erpnext/translations/zh-TW.csv

* chore: Resolve conflicts

---------

Co-authored-by: abdosaeed95 <118386543+abdosaeed95@users.noreply.github.com>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-08-01 11:19:38 +05:30
ruthra kumar
07f1b42c64 Merge pull request #36390 from frappe/mergify/bp/version-14-hotfix/pr-36382
refactor(test): introduce and make use of mixins in unit tests (backport #36382)
2023-08-01 11:08:20 +05:30
ruthra kumar
3a024294a9 Merge pull request #36436 from frappe/mergify/bp/version-14-hotfix/pr-36434
fix: incorrect usage `get_cached_value` on single doctypes (backport #36434)
2023-08-01 10:42:03 +05:30
mergify[bot]
5b562961e3 fix: root type in account map for balance sheet (#36303)
* fix: root type in account map for balance sheet (#36303)

* fix: root type in account map

* fix: fetch gle by root type in consolidated financial statement

* refactor: consolidated financial statement gle query

* fix: filter accounts by root type

(cherry picked from commit 11bd15e580)

# Conflicts:
#	erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py

* chore: Resolve conflicts

---------

Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-08-01 10:14:34 +05:30
ruthra kumar
8f72a6814b fix: incorrect usage get_cached_value on single doctypes
(cherry picked from commit ba15810639)
2023-08-01 10:12:55 +05:30
mergify[bot]
99e7406b5b fix: not able to make material request (backport #36416) (#36426)
fix: not able to make material request (#36416)

(cherry picked from commit f83a100a8d)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2023-07-31 19:43:29 +05:30
mergify[bot]
516191bf2b fix: group item reorder by (warehouse, material_request_type) (backport #35818) (#36425)
fix: group item reorder by (warehouse, material_request_type) (#35818)

* fix: group item reorder by (warehouse, material_request_type)

* fix: update reorder error message

* chore: linter

* fix: correct error message

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>

* chore: linter

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
(cherry picked from commit e8eeeb16e2)

Co-authored-by: Devin Slauenwhite <devin.slauenwhite@gmail.com>
2023-07-31 18:36:09 +05:30
mergify[bot]
33a947726d fix: Defined "Open" Status as default (backport #36421) (#36424)
fix: Defined "Open" Status as default (#36421)

Defined "Open" Status as default of the child doctype (Quality Review Objective), because without it the main doctype (Quality Review) has "Passed" status.
This happens because in the "set_status" function, the status is updated according to the status of the child records.

(cherry picked from commit 652398fad2)

Co-authored-by: xdlumertz <alexandrelumertz@gmail.com>
2023-07-31 18:32:29 +05:30
mergify[bot]
29ddd26ba1 fix: job card suggest holiday as start date (backport #35958) (#36423)
fix: job card suggest holiday as start date (#35958)

(cherry picked from commit ce36d1f668)

Co-authored-by: Vimal <mailtovimal@gmail.com>
2023-07-31 18:22:44 +05:30
mergify[bot]
26a0b3b380 perf: use LEFT JOIN instead of NOT EXISTS (backport #36221) (#36383)
* perf: use `LEFT JOIN` instead of `NOT EXISTS`

(cherry picked from commit 58d867503b)

# Conflicts:
#	erpnext/manufacturing/doctype/bom_update_log/bom_updation_utils.py

* fix: long queue `process_boms_cost_level_wise`

(cherry picked from commit 148d466ae5)

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-07-31 17:39:11 +05:30
mergify[bot]
289dc36696 fix: process_owner is not link User (backport #36420) (#36422)
fix: process_owner is not link User (#36420)

-Changed "fetch from" since field is not a binding field
-Change field "full_name" from Hidden to Read Only

(cherry picked from commit 05b07e098a)

Co-authored-by: xdlumertz <alexandrelumertz@gmail.com>
2023-07-31 17:38:02 +05:30
mergify[bot]
cd3b85ccff fix: in payment_entry 'Unallocated Amount' cal is broken (#36369)
fix: in payment_entry 'Unallocated Amount' cal is broken

(cherry picked from commit f9fa34ff40)

Co-authored-by: Ashish Shah <mr.ashish.shah@gmail.com>
2023-07-30 18:43:01 +05:30
Deepesh Garg
09966d10d4 Merge pull request #36409 from frappe/mergify/bp/version-14-hotfix/pr-36320
fix(Supplier): german translations (#36320)
2023-07-30 18:42:33 +05:30
barredterra
9c8e2a33e9 fix: german translations
(cherry picked from commit 3558c3d24e)
2023-07-30 09:11:08 +00:00
mergify[bot]
a799e1b217 fix: multiple issues related to Production Plan (backport #36377) (#36381)
* fix: multiple issues related to Production Plan

(cherry picked from commit 1c2148b637)

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

* chore: fixed conflicts

---------

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2023-07-30 10:56:49 +05:30
rohitwaghchaure
4fad4c3fa4 Merge pull request #36404 from rohitwaghchaure/fixed-incorrect-qty-set-based-on-serial-no
fix: incorrect qty set in the serial no picker
2023-07-30 10:56:04 +05:30
Rohit Waghchaure
57b19a523e fix: incorrect qty set in the serial no picker 2023-07-29 19:42:23 +05:30
rohitwaghchaure
b72232b358 Merge pull request #36401 from frappe/mergify/bp/version-14-hotfix/pr-36375
fix: Job Card validation fixed when displaying total completed quantity (backport #36375)
2023-07-29 19:03:54 +05:30
mergify[bot]
32bdb7cccd fix: only publish repost progress to doc subscriber (backport #36400) (#36402)
* fix: only publish repost progress to doc subscriber (#36400)

Huge size of string gets blasted to everyone on site. Due to some memory
leak (cause unknown) till sockets are open the strings are also in
process' memory.

related https://github.com/frappe/frappe/issues/21863

(cherry picked from commit c0642cf528)

# Conflicts:
#	erpnext/stock/stock_ledger.py

* chore: conflicts

---------

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-07-29 18:14:51 +05:30
ramonalmato
7b3bcd3bc4 fix: Job Card validation fixed when displaying total completed quantity
(cherry picked from commit 49981fecc7)
2023-07-29 09:32:10 +00:00
rohitwaghchaure
cfa07bed74 Merge pull request #36388 from frappe/mergify/bp/version-14-hotfix/pr-36380
fix: change fieldtype from Currency to Float for the valuation rate in reports (backport #36380)
2023-07-29 12:06:37 +05:30
ruthra kumar
2844d849e0 refactor(test): introduce and make use of mixins in unit tests (#36382)
* refactor(test): create and use test mixin

* chore(test): replace get_user_default with variable

(cherry picked from commit 3b58055410)
2023-07-28 15:33:20 +00:00
Rohit Waghchaure
93bd4c7ff3 fix: change fieldtype from Currency to Float for the valuation rate in the stock report
(cherry picked from commit c82cb379a5)
2023-07-28 15:06:59 +00:00
mergify[bot]
6d051f5732 fix: removed "fetch_from" (backport #36365) (#36386)
fix: removed "fetch_from"

* fix: removed ("fetch_from": "goal.objective")
The field ended up being disabled because of this.

(cherry picked from commit 1c687a4afd)

Co-authored-by: xdlumertz <alexandrelumertz@gmail.com>
2023-07-28 18:15:06 +05:30
mergify[bot]
4ac60cd73b perf: move project status reminder to hourly (backport #36372) (#36373)
perf: move project status reminder to hourly (#36372)

Only used for sending daily/weekly/bi-daily

[skip ci]

(cherry picked from commit e36c8ce5be)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-07-28 12:54:39 +05:30
Deepesh Garg
e8b93ef32f Merge pull request #36362 from frappe/mergify/bp/version-14-hotfix/pr-36092
fix: paid_amount when the group is mode of payment (backport #36092)
2023-07-27 22:35:13 +05:30
Deepesh Garg
ddcd2cfe80 Merge pull request #36358 from frappe/mergify/bp/version-14-hotfix/pr-36347
fix: Ignore account closing balance for financial statement (#36347)
2023-07-27 22:34:52 +05:30
Deepesh Garg
e25f4ffa50 Merge pull request #36361 from frappe/mergify/bp/version-14-hotfix/pr-36313
fix(Item Group): allow root deletion (#36313)
2023-07-27 22:24:56 +05:30
HarryPaulo
50ef35845a fix: paid_amount when the group is mode of payment
(cherry picked from commit 2268f7db43)
2023-07-27 15:53:00 +00:00
barredterra
1a6be5e19b fix(Item Group): allow root deletion
It was not possible to delete an empty, unused Item Group without any
children, if it was one of possibly multiple roots of the Item Group tree.
This fix allows deleting a root Item Group.

(cherry picked from commit fd2c272bed)
2023-07-27 15:45:34 +00:00
Deepesh Garg
be94402338 chore: resolve conflicts 2023-07-27 21:12:58 +05:30
Deepesh Garg
800417eeed fix: Ignore account closing balance for financial statement
(cherry picked from commit ccf1920a78)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
2023-07-27 14:25:06 +00:00
Deepesh Garg
dc603c3df0 Merge pull request #36297 from GursheenK/cost-center-check-for-payment-reconciliation
fix: cost center filter for fetching payments
2023-07-27 18:36:12 +05:30
Deepesh Garg
8bf957b9a1 Merge pull request #36342 from frappe/mergify/bp/version-14-hotfix/pr-36327
fix: GL Entries should not be split based on cost center allocation in PCV (#36327)
2023-07-27 12:00:59 +05:30
Nabin Hait
ade13e6d36 fix: GL Entries should not be splitted based on cost center allocation in PCV
(cherry picked from commit 666d961875)
2023-07-27 06:03:56 +00:00
Deepesh Garg
c3c7dd89fb Merge pull request #36321 from frappe/mergify/bp/version-14-hotfix/pr-36318
chore(Item Group): remove redundant autoname (backport #36318)
2023-07-26 22:27:29 +05:30
mergify[bot]
b105ba11b8 chore: adding totals in asset reports (backport #36334) (#36335)
chore: adding totals in asset reports (#36334)

(cherry picked from commit 5e7b05e566)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-07-26 22:25:39 +05:30
Deepesh Garg
faf692d344 Merge pull request #36325 from GursheenK/tds-tcs-payable-monthly-report
fix: show TDS & TCS separately in TDS payable monthly
2023-07-26 16:20:01 +05:30
ruthra kumar
aadd3f9e69 Merge pull request #36323 from frappe/mergify/bp/version-14-hotfix/pr-36298
fix: show invoices name instead of object address (backport #36298)
2023-07-26 12:20:15 +05:30
Frappe PR Bot
d9aa4057d7 chore(release): Bumped to Version 14.32.1
## [14.32.1](https://github.com/frappe/erpnext/compare/v14.32.0...v14.32.1) (2023-07-26)

### Bug Fixes

* filter by cost center in trial balance ([a20d78a](a20d78ad9e))
2023-07-26 06:28:35 +00:00
Deepesh Garg
bc2c7fe315 Merge pull request #36326 from frappe/mergify/bp/version-14/pr-36324
fix: filter by cost center in trial balance (#36324)
2023-07-26 11:57:02 +05:30
Gursheen Anand
a20d78ad9e fix: filter by cost center in trial balance
(cherry picked from commit 02428b446d)
2023-07-26 06:25:51 +00:00
Deepesh Garg
4b49864995 Merge pull request #36324 from GursheenK/cost-center-filter-in-TB
fix: filter by cost center in trial balance
2023-07-26 11:55:03 +05:30
gouravengineer
e802f0c352 fix: show invoices name instead of object address
comma_and function in expecting a list but it gets a tuple so it is returning a object instead of a string

(cherry picked from commit cf93714a7c)
2023-07-26 11:39:13 +05:30
Gursheen Anand
619b0feb5f fix: show tds & tcs separately 2023-07-26 11:35:02 +05:30
Gursheen Anand
32fea643cd feat: add party type filter 2023-07-26 11:34:27 +05:30
Gursheen Anand
02428b446d fix: filter by cost center in trial balance 2023-07-26 11:04:46 +05:30
Gursheen Anand
479cab0336 fix: test with None conditon in PE 2023-07-26 09:56:48 +05:30
barredterra
f07b87c5eb chore(Item Group): remove redundant autoname
(cherry picked from commit 1691eee26e)
2023-07-26 04:15:22 +00:00
Frappe PR Bot
c4b0790003 chore(release): Bumped to Version 14.32.0
# [14.32.0](https://github.com/frappe/erpnext/compare/v14.31.3...v14.32.0) (2023-07-26)

### Bug Fixes

* added missing option Partially Received in the status dropdown field ([4fa93b0](4fa93b05c6))
* allocation logic on 'Get Outstanding Invoices' btn in PE ([14600fa](14600fa190))
* allow both custodian and location while creating asset ([#36263](https://github.com/frappe/erpnext/issues/36263)) ([2b47f58](2b47f5815e))
* Ambiguous column error while submitting stock entry (backport [#36209](https://github.com/frappe/erpnext/issues/36209)) ([#36222](https://github.com/frappe/erpnext/issues/36222)) ([844ec58](844ec58f6b))
* apply discount on item after applying price list ([#36316](https://github.com/frappe/erpnext/issues/36316)) ([cff6e72](cff6e72838))
* apply terms validaton only in Sales/Purchase documents ([8f24292](8f24292155))
* broken overallocation validation in payment entry ([e1d31aa](e1d31aad4d))
* clear accounting dimension value when based on field changes ([1fe6d4e](1fe6d4ef1f))
* customer filter in process soa ([5938af9](5938af9c3f))
* Default fiscal year in accounting, buying and sellingcharts ([8ca3d6b](8ca3d6b7f3))
* fetch acc dimension fieldname ([542c804](542c80433d))
* FY in naming series variable for orders ([6a6a3ae](6a6a3ae3c3))
* group by in fixed asset register ([#36310](https://github.com/frappe/erpnext/issues/36310)) ([6cca18e](6cca18e168))
* Ignore permissions while submitting account closing balance record ([#35536](https://github.com/frappe/erpnext/issues/35536)) ([c2976ef](c2976ef643))
* itemised tax breakup ([6f2e639](6f2e639182))
* made item or warehouse filter mandatory (backport [#36208](https://github.com/frappe/erpnext/issues/36208)) ([#36215](https://github.com/frappe/erpnext/issues/36215)) ([57cf3c2](57cf3c28f8))
* no default email account causing reposting issue ([026f9f5](026f9f5a69))
* **regional:** set `frappe.flags.company` temporarily, where required ([73e9b38](73e9b38cda))
* set new purchase_receipt_amount on asset split ([#36272](https://github.com/frappe/erpnext/issues/36272)) ([e867fe7](e867fe77a4))
* timeout error while cancelling the Purchase Receipt ([0575005](0575005c87))
* Trial Balance report considering cancelled entries ([ca4f86d](ca4f86d5af))

### Features

* filter based on accounting dimension in profitability analysis ([ba95df2](ba95df2a74))
2023-07-26 04:10:45 +00:00
Deepesh Garg
55f553fb09 Merge pull request #36293 from frappe/version-14-hotfix
chore: release v14
2023-07-26 09:39:11 +05:30
Anand Baburajan
cff6e72838 fix: apply discount on item after applying price list (#36316) 2023-07-25 21:55:26 +05:30
Anand Baburajan
6cca18e168 fix: group by in fixed asset register (#36310) 2023-07-25 20:58:18 +05:30
ruthra kumar
2e5c531bbf Merge pull request #36298 from gouravengineer/show_invoice_name_instead_of_object
fix:show invoices name instead of object address
2023-07-25 20:38:53 +05:30
rohitwaghchaure
80e5a47ff6 Merge pull request #36300 from rohitwaghchaure/fixed-added-missing-options-partially-received
fix: added missing option Partially Received in the status field
2023-07-25 18:08:18 +05:30
Rohit Waghchaure
4fa93b05c6 fix: added missing option Partially Received in the status dropdown field 2023-07-25 17:22:56 +05:30
rohitwaghchaure
ab757cbd82 Merge pull request #36289 from rohitwaghchaure/fixed-purchase-receipt-timeout-on-cancel
fix: timeout error while cancelling the Purchase Receipt
2023-07-25 17:13:18 +05:30
Gursheen Anand
c3b21a6c30 chore: linting issues 2023-07-25 17:04:03 +05:30
Gursheen Anand
c9daa85985 fix: cost center filter for fetching payments 2023-07-25 16:50:46 +05:30
gouravengineer
cf93714a7c fix:show invoices name instead of object address
comma_and function in expecting a list but it gets a tuple so it is returning a object instead of a string
2023-07-25 16:23:36 +05:30
Deepesh Garg
82f05bf11a Merge pull request #36292 from frappe/mergify/bp/version-14-hotfix/pr-36107
feat: filtering based on accounting dimensions in profitability analysis (backport #36107)
2023-07-25 15:18:23 +05:30
Gursheen Anand
542c80433d fix: fetch acc dimension fieldname
(cherry picked from commit 1c5c310f5a)
2023-07-25 08:59:30 +00:00
Gursheen Anand
1fe6d4ef1f fix: clear accounting dimension value when based on field changes
(cherry picked from commit 21c993a7b3)
2023-07-25 08:59:30 +00:00
Gursheen Anand
ba95df2a74 feat: filter based on accounting dimension in profitability analysis
(cherry picked from commit dd8c3d5462)
2023-07-25 08:59:29 +00:00
Deepesh Garg
dc69f47aab Merge pull request #36286 from resilient-tech/fix-itemised-tax-breakup
fix: Correct Tax Breakup for different tax rates for same hsn code (#36235)
2023-07-25 14:22:34 +05:30
Rohit Waghchaure
0575005c87 fix: timeout error while cancelling the Purchase Receipt 2023-07-25 12:00:36 +05:30
DaizyModi
6f2e639182 fix: itemised tax breakup 2023-07-25 11:44:48 +05:30
Deepesh Garg
ed0d0d8e8e Merge pull request #36282 from frappe/mergify/bp/version-14-hotfix/pr-36278
fix(regional): set `frappe.flags.company` temporarily, where required (#36278)
2023-07-24 20:57:05 +05:30
rohitwaghchaure
4c35942c2d Merge pull request #36265 from frappe/mergify/bp/version-14-hotfix/pr-36259
fix: no default email account causing reposting issue (backport #36259)
2023-07-24 20:02:30 +05:30
Sagar Vora
73e9b38cda fix(regional): set frappe.flags.company temporarily, where required
(cherry picked from commit 4205f564a0)
2023-07-24 14:05:34 +00:00
Anand Baburajan
e867fe77a4 fix: set new purchase_receipt_amount on asset split (#36272) 2023-07-24 18:50:16 +05:30
Deepesh Garg
6948acbb69 Merge pull request #36275 from frappe/mergify/bp/version-14-hotfix/pr-36274
fix: customer filter in process soa (#36274)
2023-07-24 17:36:19 +05:30
Gursheen Anand
5938af9c3f fix: customer filter in process soa
(cherry picked from commit 34d7fb388d)
2023-07-24 12:03:04 +00:00
Deepesh Garg
c575c88ab9 Merge pull request #36258 from frappe/mergify/bp/version-14-hotfix/pr-36251
fix: unresponsive sales invoice form (#36251)
2023-07-24 17:32:36 +05:30
Anand Baburajan
2b47f5815e fix: allow both custodian and location while creating asset (#36263) 2023-07-24 15:51:16 +05:30
Rohit Waghchaure
026f9f5a69 fix: no default email account causing reposting issue
(cherry picked from commit efb51526a9)
2023-07-24 09:34:09 +00:00
ruthra kumar
b849f6c4d6 Merge pull request #36252 from frappe/mergify/bp/version-14-hotfix/pr-36241
fix: multiple fixes on payment terms based issues in payment entry (backport #36241)
2023-07-24 14:49:55 +05:30
ruthra kumar
8f24292155 fix: apply terms validaton only in Sales/Purchase documents 2023-07-24 14:13:57 +05:30
ruthra kumar
bf948243a6 refactor: refresh table once after loop ends
(cherry picked from commit d048365da3)
2023-07-24 08:31:57 +00:00
ruthra kumar
690b52622d refactor: handle references without any template and payment_term
(cherry picked from commit ec7558b9e0)
2023-07-24 07:06:34 +00:00
ruthra kumar
14600fa190 fix: allocation logic on 'Get Outstanding Invoices' btn in PE
1. fixed broken `payment_term` filter in Payment References section
2. Throw error if user fails to select 'Payment Term' for an invoice
with 'Payment Term based allocation' enabled.

(cherry picked from commit 662ccd467c)
2023-07-24 07:06:34 +00:00
ruthra kumar
3d661709fa Merge pull request #36237 from frappe/mergify/bp/version-14-hotfix/pr-36206
test: overallocation validation in payment entry (backport #36206)
2023-07-22 11:23:11 +05:30
ruthra kumar
25301881c9 chore(test): enable multi-currency party for testing
(cherry picked from commit 93246043ec)
2023-07-22 05:12:13 +00:00
ruthra kumar
bc7ab2f787 chore: validation on multi-currency tran on company curtency account
(cherry picked from commit 8f9ef4ef5b)
2023-07-22 05:12:12 +00:00
ruthra kumar
602f0769c4 chore: use flt for currency
(cherry picked from commit 5b37919574)
2023-07-22 05:12:12 +00:00
ruthra kumar
f3295a9f59 chore: test more scenarios
(cherry picked from commit 6b4a81ee48)
2023-07-22 05:12:12 +00:00
ruthra kumar
2f4d8e1e94 test: overallocation validation in payment entry
(cherry picked from commit e7e3853f81)
2023-07-22 05:12:11 +00:00
Deepesh Garg
bd9e0e8b5a chore: release version 14 (#36233)
* fix: broken overallocation validation in payment entry

In a multi term payment schedule, overallocation logic broke. Fixing
it using individual term outstanding amount in references. this should
work for the simple, one term payment schedule as well

(cherry picked from commit f8d4b19cb9)

* refactor: payment term outstanding in party account currency

(cherry picked from commit ee83f94bb0)

* fix: Default fiscal year in accounting, buying and sellingcharts

(cherry picked from commit 3759a41b83)

* fix: Trial Balance report considering cancelled entries

(cherry picked from commit fd58bbff6b)

* fix: made item or warehouse filter mandatory (backport #36208) (#36215)

fix: made item or warehouse filter mandatory

(cherry picked from commit 16498627ce)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>

* fix: Ambiguous column error while submitting stock entry (backport #36209) (#36222)

fix: Ambiguous column error while submitting stock entry

Stock Entry Type=Manufacture

request.js:457 Traceback (most recent call last):
  File "apps/frappe/frappe/app.py", line 94, in application
    response = frappe.api.handle()
  File "apps/frappe/frappe/api.py", line 54, in handle
    return frappe.handler.handle()
  File "apps/frappe/frappe/handler.py", line 47, in handle
    data = execute_cmd(cmd)
  File "apps/frappe/frappe/handler.py", line 85, in execute_cmd
    return frappe.call(method, **frappe.form_dict)
  File "apps/frappe/frappe/__init__.py", line 1610, in call
    return fn(*args, **newargs)
  File "apps/frappe/frappe/desk/form/save.py", line 28, in savedocs
    doc.save()
  File "apps/frappe/frappe/model/document.py", line 305, in save
    return self._save(*args, **kwargs)
  File "apps/frappe/frappe/model/document.py", line 327, in _save
    return self.insert()
  File "apps/frappe/frappe/model/document.py", line 259, in insert
    self.run_before_save_methods()
  File "apps/frappe/frappe/model/document.py", line 1045, in run_before_save_methods
    self.run_method("validate")
  File "apps/frappe/frappe/model/document.py", line 914, in run_method
    out = Document.hook(fn)(self, *args, **kwargs)
  File "apps/frappe/frappe/model/document.py", line 1264, in composer
    return composed(self, method, *args, **kwargs)
  File "apps/frappe/frappe/model/document.py", line 1246, in runner
    add_to_return_value(self, fn(self, *args, **kwargs))
  File "apps/frappe/frappe/model/document.py", line 911, in fn
    return method_object(*args, **kwargs)
  File "apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry.py", line 122, in validate
    self.validate_qty()
  File "apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry.py", line 433, in validate_qty
    transferred_materials = frappe.db.sql(
  File "apps/frappe/frappe/database/database.py", line 220, in sql
    self._cursor.execute(query, values)
  File "env/lib/python3.10/site-packages/pymysql/cursors.py", line 158, in execute
    result = self._query(query)
  File "env/lib/python3.10/site-packages/pymysql/cursors.py", line 325, in _query
    conn.query(q)
  File "env/lib/python3.10/site-packages/pymysql/connections.py", line 549, in query
    self._affected_rows = self._read_query_result(unbuffered=unbuffered)
  File "env/lib/python3.10/site-packages/pymysql/connections.py", line 779, in _read_query_result
    result.read()
  File "env/lib/python3.10/site-packages/pymysql/connections.py", line 1157, in read
    first_packet = self.connection._read_packet()
  File "env/lib/python3.10/site-packages/pymysql/connections.py", line 729, in _read_packet
    packet.raise_for_error()
  File "env/lib/python3.10/site-packages/pymysql/protocol.py", line 221, in raise_for_error
    err.raise_mysql_exception(self._data)
  File "env/lib/python3.10/site-packages/pymysql/err.py", line 143, in raise_mysql_exception
    raise errorclass(errno, errval)
pymysql.err.OperationalError: (1052, "Column 'qty' in field list is ambiguous")

(cherry picked from commit c21fd45883)

Co-authored-by: MohsinAli <mmatiyailol@gmail.com>

* fix: Ignore permissions while submitting account closing balance record (#35536)

(cherry picked from commit f11d9b019d)

* fix: FY in naming series variable for orders

(cherry picked from commit 7a7d32db81)

---------

Co-authored-by: ruthra kumar <ruthra@erpnext.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
Co-authored-by: MohsinAli <mmatiyailol@gmail.com>
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2023-07-21 16:15:31 +05:30
Deepesh Garg
62bc75a917 Merge branch 'version-14' of https://github.com/frappe/erpnext into version-14-hotfix 2023-07-21 16:14:23 +05:30
Deepesh Garg
51f7e6e648 Merge pull request #36231 from frappe/mergify/bp/version-14-hotfix/pr-36229
fix: FY in naming series variable for orders (backport #36229)
2023-07-21 16:06:58 +05:30
Deepesh Garg
6a6a3ae3c3 fix: FY in naming series variable for orders
(cherry picked from commit 7a7d32db81)
2023-07-21 10:36:30 +00:00
Deepesh Garg
ae494b8ee8 Merge pull request #36226 from frappe/mergify/bp/version-14-hotfix/pr-35536
fix: Ignore permissions while submitting account closing balance record (#35536)
2023-07-21 12:47:14 +05:30
Nabin Hait
c2976ef643 fix: Ignore permissions while submitting account closing balance record (#35536)
(cherry picked from commit f11d9b019d)
2023-07-21 05:26:15 +00:00
mergify[bot]
844ec58f6b fix: Ambiguous column error while submitting stock entry (backport #36209) (#36222)
fix: Ambiguous column error while submitting stock entry

Stock Entry Type=Manufacture

request.js:457 Traceback (most recent call last):
  File "apps/frappe/frappe/app.py", line 94, in application
    response = frappe.api.handle()
  File "apps/frappe/frappe/api.py", line 54, in handle
    return frappe.handler.handle()
  File "apps/frappe/frappe/handler.py", line 47, in handle
    data = execute_cmd(cmd)
  File "apps/frappe/frappe/handler.py", line 85, in execute_cmd
    return frappe.call(method, **frappe.form_dict)
  File "apps/frappe/frappe/__init__.py", line 1610, in call
    return fn(*args, **newargs)
  File "apps/frappe/frappe/desk/form/save.py", line 28, in savedocs
    doc.save()
  File "apps/frappe/frappe/model/document.py", line 305, in save
    return self._save(*args, **kwargs)
  File "apps/frappe/frappe/model/document.py", line 327, in _save
    return self.insert()
  File "apps/frappe/frappe/model/document.py", line 259, in insert
    self.run_before_save_methods()
  File "apps/frappe/frappe/model/document.py", line 1045, in run_before_save_methods
    self.run_method("validate")
  File "apps/frappe/frappe/model/document.py", line 914, in run_method
    out = Document.hook(fn)(self, *args, **kwargs)
  File "apps/frappe/frappe/model/document.py", line 1264, in composer
    return composed(self, method, *args, **kwargs)
  File "apps/frappe/frappe/model/document.py", line 1246, in runner
    add_to_return_value(self, fn(self, *args, **kwargs))
  File "apps/frappe/frappe/model/document.py", line 911, in fn
    return method_object(*args, **kwargs)
  File "apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry.py", line 122, in validate
    self.validate_qty()
  File "apps/erpnext/erpnext/stock/doctype/stock_entry/stock_entry.py", line 433, in validate_qty
    transferred_materials = frappe.db.sql(
  File "apps/frappe/frappe/database/database.py", line 220, in sql
    self._cursor.execute(query, values)
  File "env/lib/python3.10/site-packages/pymysql/cursors.py", line 158, in execute
    result = self._query(query)
  File "env/lib/python3.10/site-packages/pymysql/cursors.py", line 325, in _query
    conn.query(q)
  File "env/lib/python3.10/site-packages/pymysql/connections.py", line 549, in query
    self._affected_rows = self._read_query_result(unbuffered=unbuffered)
  File "env/lib/python3.10/site-packages/pymysql/connections.py", line 779, in _read_query_result
    result.read()
  File "env/lib/python3.10/site-packages/pymysql/connections.py", line 1157, in read
    first_packet = self.connection._read_packet()
  File "env/lib/python3.10/site-packages/pymysql/connections.py", line 729, in _read_packet
    packet.raise_for_error()
  File "env/lib/python3.10/site-packages/pymysql/protocol.py", line 221, in raise_for_error
    err.raise_mysql_exception(self._data)
  File "env/lib/python3.10/site-packages/pymysql/err.py", line 143, in raise_mysql_exception
    raise errorclass(errno, errval)
pymysql.err.OperationalError: (1052, "Column 'qty' in field list is ambiguous")

(cherry picked from commit c21fd45883)

Co-authored-by: MohsinAli <mmatiyailol@gmail.com>
2023-07-20 20:15:36 +05:30
Frappe PR Bot
bafb79db50 chore(release): Bumped to Version 14.31.3
## [14.31.3](https://github.com/frappe/erpnext/compare/v14.31.2...v14.31.3) (2023-07-20)

### Bug Fixes

* Trial Balance report considering cancelled entries ([c5e30f5](c5e30f5336))
2023-07-20 14:42:49 +00:00
Deepesh Garg
1d108ec224 Merge pull request #36219 from frappe/mergify/bp/version-14/pr-36217
fix: Trial Balance report considering canceled entries (#36214)
2023-07-20 20:11:12 +05:30
mergify[bot]
57cf3c28f8 fix: made item or warehouse filter mandatory (backport #36208) (#36215)
fix: made item or warehouse filter mandatory

(cherry picked from commit 16498627ce)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2023-07-20 20:11:05 +05:30
Deepesh Garg
c5e30f5336 fix: Trial Balance report considering cancelled entries
(cherry picked from commit fd58bbff6b)
(cherry picked from commit ca4f86d5af)
2023-07-20 13:28:40 +00:00
Deepesh Garg
4cfecdc66b Merge pull request #36217 from frappe/mergify/bp/version-14-hotfix/pr-36214
fix: Trial Balance report considering cancelled entries (#36214)
2023-07-20 18:57:55 +05:30
Deepesh Garg
ca4f86d5af fix: Trial Balance report considering cancelled entries
(cherry picked from commit fd58bbff6b)
2023-07-20 13:18:53 +00:00
Frappe PR Bot
d6168ca979 chore(release): Bumped to Version 14.31.2
## [14.31.2](https://github.com/frappe/erpnext/compare/v14.31.1...v14.31.2) (2023-07-19)

### Bug Fixes

* Default fiscal year in accounting, buying and sellingcharts ([ceea8be](ceea8be483))
2023-07-19 08:10:33 +00:00
Deepesh Garg
78a2333109 Merge pull request #36191 from frappe/mergify/bp/version-14/pr-36190
fix: Default fiscal year in accounting, buying and selling charts (#36189)
2023-07-19 13:38:57 +05:30
Deepesh Garg
ceea8be483 fix: Default fiscal year in accounting, buying and sellingcharts
(cherry picked from commit 3759a41b83)
(cherry picked from commit 8ca3d6b7f3)
2023-07-19 08:02:18 +00:00
Deepesh Garg
bd82ae523d Merge pull request #36190 from frappe/mergify/bp/version-14-hotfix/pr-36189
fix: Default fiscal year in accounting, buying and selling charts (backport #36189)
2023-07-19 13:31:49 +05:30
Deepesh Garg
8ca3d6b7f3 fix: Default fiscal year in accounting, buying and sellingcharts
(cherry picked from commit 3759a41b83)
2023-07-19 07:58:59 +00:00
Frappe PR Bot
388bbca492 chore(release): Bumped to Version 14.31.1
## [14.31.1](https://github.com/frappe/erpnext/compare/v14.31.0...v14.31.1) (2023-07-19)

### Bug Fixes

* broken overallocation validation in payment entry ([ff0b51e](ff0b51ecdb))
2023-07-19 05:42:32 +00:00
ruthra kumar
1641630d70 Merge pull request #36185 from frappe/mergify/bp/version-14/pr-36181
fix: broken overallocation validation in payment entry (backport #36181)
2023-07-19 11:10:57 +05:30
ruthra kumar
61456ec45d Merge pull request #36184 from frappe/mergify/bp/version-14-hotfix/pr-36181
fix: broken overallocation validation in payment entry (backport #36181)
2023-07-19 11:10:41 +05:30
ruthra kumar
dd25c83c30 refactor: payment term outstanding in party account currency
(cherry picked from commit ee83f94bb0)
2023-07-19 05:17:40 +00:00
ruthra kumar
ff0b51ecdb fix: broken overallocation validation in payment entry
In a multi term payment schedule, overallocation logic broke. Fixing
it using individual term outstanding amount in references. this should
work for the simple, one term payment schedule as well

(cherry picked from commit f8d4b19cb9)
2023-07-19 05:17:39 +00:00
ruthra kumar
fcafff7ebe refactor: payment term outstanding in party account currency
(cherry picked from commit ee83f94bb0)
2023-07-19 05:17:22 +00:00
ruthra kumar
e1d31aad4d fix: broken overallocation validation in payment entry
In a multi term payment schedule, overallocation logic broke. Fixing
it using individual term outstanding amount in references. this should
work for the simple, one term payment schedule as well

(cherry picked from commit f8d4b19cb9)
2023-07-19 05:17:22 +00:00
Frappe PR Bot
22a19e4b6e chore(release): Bumped to Version 14.31.0
# [14.31.0](https://github.com/frappe/erpnext/compare/v14.30.7...v14.31.0) (2023-07-18)

### Bug Fixes

* `TypeError` while creating WO from PP (backport [#36136](https://github.com/frappe/erpnext/issues/36136)) ([#36137](https://github.com/frappe/erpnext/issues/36137)) ([dd51010](dd5101056d))
* Account balance patch and query fixes ([#36117](https://github.com/frappe/erpnext/issues/36117)) ([0147754](0147754273))
* Accounts closing balance patch ([#36113](https://github.com/frappe/erpnext/issues/36113)) ([cf29156](cf29156139))
* allow manual asset receipt mov from nowhere ([#36093](https://github.com/frappe/erpnext/issues/36093)) ([4aaa1a1](4aaa1a15d7))
* Ambiguous column error while submitting stock entry ([#36058](https://github.com/frappe/erpnext/issues/36058)) ([a3a052b](a3a052bb51))
* circular dependency during reposting causing timeout error ([2c21404](2c21404813))
* get_dimension with_cost_center_and_project=false is not working ([#35974](https://github.com/frappe/erpnext/issues/35974)) ([42c93a1](42c93a1f00))
* Handle multi-company in patch ([#36127](https://github.com/frappe/erpnext/issues/36127)) ([e7f5754](e7f57542ab))
* improve "Update Items" modal ([#36105](https://github.com/frappe/erpnext/issues/36105)) ([b86571d](b86571d9d4))
* incorrect Balance Qty in the stock ledger for the item with Serial and Batch no ([373b868](373b868c1d))
* incorrect Reserved Qty for Production Plan in BIN for multi-uom case ([2f632d0](2f632d031a))
* Opening balance in TB report ([#36171](https://github.com/frappe/erpnext/issues/36171)) ([313ad7a](313ad7ae89))
* Opening entries showing up incorrectly in TB report ([#36135](https://github.com/frappe/erpnext/issues/36135)) ([d10e5e6](d10e5e666b))
* Remove current fiscal year from Global Defaults ([#35960](https://github.com/frappe/erpnext/issues/35960)) ([b877fa6](b877fa60db))
* Trailing opening entries in Accounts closing balance ([#36175](https://github.com/frappe/erpnext/issues/36175)) ([8a607db](8a607db493))
* validate docs in closed accounting period on save ([#36157](https://github.com/frappe/erpnext/issues/36157)) ([5985e02](5985e02574))

### Features

* add local holidays ([#36116](https://github.com/frappe/erpnext/issues/36116)) ([94caf7f](94caf7f5a8))
* add project filter in reports importing financial statements js file ([#36097](https://github.com/frappe/erpnext/issues/36097)) ([63e26e3](63e26e39d4))

### Performance Improvements

* index in `Item` and `Item Variant Attribute` (backport [#36133](https://github.com/frappe/erpnext/issues/36133)) ([#36144](https://github.com/frappe/erpnext/issues/36144)) ([6198983](619898350a))
* send SLA doctypes in boot ([adb6918](adb6918834))
2023-07-18 13:59:37 +00:00
Deepesh Garg
b0f5c02d74 Merge pull request #36176 from frappe/version-14-hotfix
chore: release v14
2023-07-18 19:27:44 +05:30
Deepesh Garg
0805a8e19c Merge branch 'version-14' into version-14-hotfix 2023-07-18 17:51:19 +05:30
Anand Baburajan
5985e02574 fix: validate docs in closed accounting period on save (#36157) 2023-07-18 16:40:06 +05:30
mergify[bot]
8a607db493 fix: Trailing opening entries in Accounts closing balance (#36175)
fix: Trailing opening entries in Accounts closing balance (#36175)

(cherry picked from commit fbea61bbc6)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-18 15:20:11 +05:30
Frappe PR Bot
cdd5f992f6 chore(release): Bumped to Version 14.30.7
## [14.30.7](https://github.com/frappe/erpnext/compare/v14.30.6...v14.30.7) (2023-07-18)

### Bug Fixes

* incorrect Reserved Qty for Production Plan in BIN for multi-uom case ([bcf7d87](bcf7d87b61))
2023-07-18 09:46:15 +00:00
rohitwaghchaure
9171f0cd9d Merge pull request #36174 from frappe/mergify/bp/version-14/pr-36162
fix: incorrect Reserved Qty for Production Plan in BIN for the multi-uom case (backport #36162)
2023-07-18 15:12:19 +05:30
mergify[bot]
ea66f18032 refactor: Leave Application should not be in hook.py (backport #36008) (#36158)
Co-authored-by: Kitti U. @ Ecosoft <kittiu@gmail.com>
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2023-07-18 14:26:47 +05:30
mergify[bot]
313ad7ae89 fix: Opening balance in TB report (#36171)
fix: Opening balance in TB report (#36171)

(cherry picked from commit cfae52a40a)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-18 14:20:28 +05:30
Rohit Waghchaure
bcf7d87b61 fix: incorrect Reserved Qty for Production Plan in BIN for multi-uom case
(cherry picked from commit 2f632d031a)
2023-07-18 08:29:02 +00:00
rohitwaghchaure
a5f398474a Merge pull request #36169 from rohitwaghchaure/fixed-incorrect-balance-qty-in-stock-ledger-entry
fix: incorrect Balance qty in the stock ledger
2023-07-17 22:44:44 +05:30
Rohit Waghchaure
373b868c1d fix: incorrect Balance Qty in the stock ledger for the item with Serial and Batch no 2023-07-17 21:07:32 +05:30
rohitwaghchaure
665898a579 Merge pull request #36162 from rohitwaghchaure/fixed-incorrect-reserved-qty-for-production-plan
fix: incorrect Reserved Qty for Production Plan in BIN for the multi-uom case
2023-07-17 20:40:25 +05:30
Rohit Waghchaure
2f632d031a fix: incorrect Reserved Qty for Production Plan in BIN for multi-uom case 2023-07-17 17:14:59 +05:30
Sagar Vora
ab3671a13b Merge pull request #36164 from frappe/mergify/bp/version-14-hotfix/pr-36163 2023-07-17 15:47:19 +05:30
Sagar Vora
30cbc682b4 chore: use consistent quotes
(cherry picked from commit bccb718cc2)
2023-07-17 10:17:00 +00:00
mergify[bot]
b877fa60db fix: Remove current fiscal year from Global Defaults (#35960)
* fix: Remove current fiscal year from Global Defaults (#35960)

* fix: Remove current fiscal year from Global Defaults

* fix: Remove button to set default

* fix: Add utils to get fiscal year

* fix: Incorrect import

* feat: Add hook for naming series parser

(cherry picked from commit 6270607c6d)

# Conflicts:
#	erpnext/accounts/doctype/fiscal_year/fiscal_year.py
#	erpnext/patches.txt
#	erpnext/public/js/utils.js

* chore: resolve conflicts

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-17 11:52:37 +05:30
mergify[bot]
c7a97cfb31 chore: add validation for account type of party type and account (#36141)
chore: add validation for account type of party type and account (#36141)

chore: add validation to check if account type of party type and account match
(cherry picked from commit 305c37917f)

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-07-17 11:35:32 +05:30
Ankush Menat
413a8a41f0 Merge pull request #36152 from frappe/mergify/bp/version-14-hotfix/pr-36150
perf: send SLA doctypes in boot (backport #36150)
2023-07-16 13:00:28 +05:30
Ankush Menat
adb6918834 perf: send SLA doctypes in boot
This request is fired on every load, data rarely if ever changes though.

(cherry picked from commit bd9ef74ef7)
2023-07-16 07:01:32 +00:00
mergify[bot]
619898350a perf: index in Item and Item Variant Attribute (backport #36133) (#36144)
* perf: index `variant_of` and `attribute` in `Item Variant Attribute`

(cherry picked from commit e4128a5c91)

* perf: index `disabled` in `Item`

(cherry picked from commit 04400eb2e4)

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-07-15 10:11:17 +05:30
mergify[bot]
68d35b08f6 refactor: Batch Item Expiry Status report (backport #36106) (#36111)
refactor: `Batch Item Expiry Status` report (#36106)

(cherry picked from commit 5f307f92e0)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-07-15 10:10:19 +05:30
mergify[bot]
94caf7f5a8 feat: add local holidays (#36116)
* feat: add local holidays

(cherry picked from commit aa18b25a71)

* test(Holiday List): weekly off and local holidays

(cherry picked from commit fd23bd0434)

* feat(Holiday List): display localized country name

(cherry picked from commit 4888d75e72)

* fix: German translations of Holiday List

(cherry picked from commit 509061f05b)

# Conflicts:
#	erpnext/translations/de.csv

* fix(Holiday List): use current user's language

For consistency with "weekly off" descriptions

(cherry picked from commit 8271a39cdb)

* fix(Holiday List): allow empty value

(cherry picked from commit 8aff5a1dab)

* refactor(Holiday List): use autocomplete fieldtype

(cherry picked from commit dab9688410)

* chore: resolve merge conflicts

---------

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
2023-07-14 20:37:48 +05:30
Frappe PR Bot
9ec3087af8 chore(release): Bumped to Version 14.30.6
## [14.30.6](https://github.com/frappe/erpnext/compare/v14.30.5...v14.30.6) (2023-07-14)

### Bug Fixes

* Opening entries showing up incorrectly in TB report ([#36135](https://github.com/frappe/erpnext/issues/36135)) ([8e48c4e](8e48c4ee3e))
2023-07-14 14:37:01 +00:00
mergify[bot]
8e48c4ee3e fix: Opening entries showing up incorrectly in TB report (#36135)
fix: Opening entries showing up incorrectly in TB report (#36135)

fix: Opening entries showing up incorrectly in TB report (#36135)

* fix: Opening entries showing up incorrectly in TB report

* chore: Linting Issue

(cherry picked from commit 297c7e833c)

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-07-14 20:05:40 +05:30
mergify[bot]
d10e5e666b fix: Opening entries showing up incorrectly in TB report (#36135)
fix: Opening entries showing up incorrectly in TB report (#36135)

* fix: Opening entries showing up incorrectly in TB report

* chore: Linting Issue

(cherry picked from commit 297c7e833c)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-14 18:46:28 +05:30
Frappe PR Bot
ed8217d309 chore(release): Bumped to Version 14.30.5
## [14.30.5](https://github.com/frappe/erpnext/compare/v14.30.4...v14.30.5) (2023-07-14)

### Bug Fixes

* `TypeError` while creating WO from PP (backport [#36136](https://github.com/frappe/erpnext/issues/36136)) (backport [#36137](https://github.com/frappe/erpnext/issues/36137)) ([#36138](https://github.com/frappe/erpnext/issues/36138)) ([c3acdcf](c3acdcf3ac))
2023-07-14 12:50:49 +00:00
mergify[bot]
c3acdcf3ac fix: TypeError while creating WO from PP (backport #36136) (backport #36137) (#36138)
fix: `TypeError` while creating WO from PP (backport #36136) (#36137)

fix: `TypeError` while creating WO from PP (#36136)

(cherry picked from commit 8f5b94f5fd)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
(cherry picked from commit dd5101056d)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-07-14 18:10:16 +05:30
mergify[bot]
dd5101056d fix: TypeError while creating WO from PP (backport #36136) (#36137)
fix: `TypeError` while creating WO from PP (#36136)

(cherry picked from commit 8f5b94f5fd)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-07-14 18:03:53 +05:30
Frappe PR Bot
d420eeb884 chore(release): Bumped to Version 14.30.4
## [14.30.4](https://github.com/frappe/erpnext/compare/v14.30.3...v14.30.4) (2023-07-14)

### Bug Fixes

* Handle multi-company in patch ([#36127](https://github.com/frappe/erpnext/issues/36127)) ([43d6cc0](43d6cc087e))
2023-07-14 10:43:22 +00:00
mergify[bot]
43d6cc087e fix: Handle multi-company in patch (#36127)
fix: Handle multi-company in patch (#36127)

* fix: Handle multi-company in patch (#36127)

fix: Handle multi-compnay in patch
(cherry picked from commit ac9ad8ec36)

* chore: re trigger patch

---------

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-07-14 16:02:32 +05:30
mergify[bot]
e7f57542ab fix: Handle multi-company in patch (#36127)
* fix: Handle multi-company in patch (#36127)

fix: Handle multi-compnay in patch
(cherry picked from commit ac9ad8ec36)

* chore: re trigger patch

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-14 15:59:57 +05:30
mergify[bot]
42c93a1f00 fix: get_dimension with_cost_center_and_project=false is not working (#35974)
fix: get_dimension with_cost_center_and_project=false is not working (#35974)

* fix: get_dimension with_cost_center_and_project=false is not working.

with_cost_center_and_project is no python str, and it always evaluated as True, despite JS call it with false

* chore: Linting Issues

---------

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

Co-authored-by: Kitti U. @ Ecosoft <kittiu@gmail.com>
2023-07-14 13:04:07 +05:30
mergify[bot]
b86571d9d4 fix: improve "Update Items" modal (#36105)
fix: improve "Update Items" modal (#36105)

* fix: make "Update Items" modal larger

* fix: remove conversion factor from overview

Conversion factor doesn't make much sense without two different UOMs
next to it, hence moving it to row detail view

(cherry picked from commit d5fe1432f8)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-07-14 13:03:26 +05:30
Ankush Menat
b167bffd22 ci: fix repo name in relase notes workflow
[skip ci]

(cherry picked from commit b5f6a1cc20)
2023-07-14 12:05:49 +05:30
Frappe PR Bot
2f2b45bd6d chore(release): Bumped to Version 14.30.3
## [14.30.3](https://github.com/frappe/erpnext/compare/v14.30.2...v14.30.3) (2023-07-14)

### Bug Fixes

* Account balance patch and query fixes ([#36117](https://github.com/frappe/erpnext/issues/36117)) ([495a8a9](495a8a9ce1))
2023-07-14 05:53:54 +00:00
mergify[bot]
495a8a9ce1 fix: Account balance patch and query fixes (#36117)
fix: Account balance patch and query fixes (#36117)

(cherry picked from commit b4bd978791)

# Conflicts:
#	erpnext/patches.txt

* chore: resolve conflicts

---------

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-07-14 11:21:59 +05:30
mergify[bot]
0147754273 fix: Account balance patch and query fixes (#36117)
* fix: Account balance patch and query fixes (#36117)

(cherry picked from commit b4bd978791)

# Conflicts:
#	erpnext/patches.txt

* chore: resolve conflicts

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-14 10:41:18 +05:30
Frappe PR Bot
1d9c28ec5e chore(release): Bumped to Version 14.30.2
## [14.30.2](https://github.com/frappe/erpnext/compare/v14.30.1...v14.30.2) (2023-07-13)

### Bug Fixes

* Accounts closing balance patch ([#36113](https://github.com/frappe/erpnext/issues/36113)) ([49f28b0](49f28b0dbb))
2023-07-13 10:52:30 +00:00
mergify[bot]
49f28b0dbb fix: Accounts closing balance patch (#36113)
fix: Accounts closing balance patch (#36113)

fix: Accounts closing balance patch (#36113)

(cherry picked from commit d631c7dffa)

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

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-07-13 16:17:04 +05:30
mergify[bot]
cf29156139 fix: Accounts closing balance patch (#36113)
fix: Accounts closing balance patch (#36113)

(cherry picked from commit d631c7dffa)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-13 16:13:23 +05:30
mergify[bot]
63e26e39d4 feat: add project filter in reports importing financial statements js file (#36097)
feat: add project filter in reports importing financial statements js file (#36097)

feat: add project filter in financial statements js file
(cherry picked from commit 596a14e34f)

Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com>
2023-07-12 19:16:10 +05:30
mergify[bot]
a3a052bb51 fix: Ambiguous column error while submitting stock entry (#36058)
fix: Ambiguous column error while submitting stock entry (#36058)
2023-07-12 13:23:07 +05:30
mergify[bot]
ea807f8d9a ci: regen release notes with GH API (backport #36098) (#36099)
ci: regen release notes with GH API (#36098)

[skip ci]

(cherry picked from commit 0340bfc90d)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-07-12 12:18:31 +05:30
Anand Baburajan
4aaa1a15d7 fix: allow manual asset receipt mov from nowhere (#36093) 2023-07-11 23:21:37 +05:30
Frappe PR Bot
d43cf0eca4 chore(release): Bumped to Version 14.30.1
## [14.30.1](https://github.com/frappe/erpnext/compare/v14.30.0...v14.30.1) (2023-07-11)

### Bug Fixes

* circular dependency during reposting causing timeout error ([026c608](026c6085cc))
2023-07-11 14:13:02 +00:00
rohitwaghchaure
a5a41f7347 Merge pull request #36091 from frappe/mergify/bp/version-14/pr-36089
fix: circular dependency during reposting causing timeout error (backport #36088) (backport #36089)
2023-07-11 19:41:35 +05:30
Rohit Waghchaure
026c6085cc fix: circular dependency during reposting causing timeout error
(cherry picked from commit c16a5814d4)
(cherry picked from commit 2c21404813)
2023-07-11 13:41:40 +00:00
rohitwaghchaure
64406a631f Merge pull request #36089 from frappe/mergify/bp/version-14-hotfix/pr-36088
fix: circular dependency during reposting causing timeout error (backport #36088)
2023-07-11 19:09:54 +05:30
Rohit Waghchaure
2c21404813 fix: circular dependency during reposting causing timeout error
(cherry picked from commit c16a5814d4)
2023-07-11 12:59:55 +00:00
Frappe PR Bot
2b72d143ca chore(release): Bumped to Version 14.30.0
# [14.30.0](https://github.com/frappe/erpnext/compare/v14.29.2...v14.30.0) (2023-07-11)

### Bug Fixes

* accepted warehouse and rejected warehouse can't be same ([5d77e3c](5d77e3ce05))
* also check on_hold ([#35910](https://github.com/frappe/erpnext/issues/35910)) ([59b3277](59b3277696))
* conflicts ([e55a264](e55a264e57))
* conflicts ([79f9785](79f9785d15))
* conflicts ([a178e66](a178e6693c))
* conflicts ([6459c28](6459c28316))
* deferred accounting entries on accounts frozen ([#35978](https://github.com/frappe/erpnext/issues/35978)) ([573183c](573183cff5))
* Delivery Note return valuation ([296f312](296f312e7f))
* German translations ([#35990](https://github.com/frappe/erpnext/issues/35990)) ([b7b864e](b7b864e68c))
* incorrect status in MR created from PP (backport [#36085](https://github.com/frappe/erpnext/issues/36085)) ([#36086](https://github.com/frappe/erpnext/issues/36086)) ([6dc7a19](6dc7a192ab))
* incorrect TCS amount while customer has advance payment ([#35397](https://github.com/frappe/erpnext/issues/35397)) ([2a4bbf3](2a4bbf34b4))
* labels and translations ([#35963](https://github.com/frappe/erpnext/issues/35963)) ([04b1d45](04b1d459eb))
* Opening balance in presentation currency in Trial Balance report ([#36036](https://github.com/frappe/erpnext/issues/36036)) ([39e38bf](39e38bf083))
* payment entry `voucher_type` error ([#35779](https://github.com/frappe/erpnext/issues/35779)) ([f3af0b2](f3af0b2d2e))
* **Payment Entry:** compare rounded amount ([#36011](https://github.com/frappe/erpnext/issues/36011)) ([d80b0aa](d80b0aa157))
* possible type error on ERR creation ([0569899](0569899499))
* precision causing outstanding issue on partly paid invoices ([#36030](https://github.com/frappe/erpnext/issues/36030)) ([3e711e8](3e711e888d))
* Share ledger showing cancelled docs ([#35993](https://github.com/frappe/erpnext/issues/35993)) ([5102d0c](5102d0c3f7))
* Validate for missing expense account ([#36078](https://github.com/frappe/erpnext/issues/36078)) ([f4f886c](f4f886c7d1))
* Vietnamese translation of "Company" ([#35887](https://github.com/frappe/erpnext/issues/35887)) ([e443e6c](e443e6c02a))

### Features

* Closing balance for period closing and reporting ([#34257](https://github.com/frappe/erpnext/issues/34257)) ([ebf3c01](ebf3c0173e))
2023-07-11 12:51:51 +00:00
Deepesh Garg
078383f086 Merge pull request #36082 from frappe/version-14-hotfix
chore: release v14
2023-07-11 18:20:08 +05:30
mergify[bot]
6dc7a192ab fix: incorrect status in MR created from PP (backport #36085) (#36086)
fix: incorrect status in MR created from PP (#36085)

(cherry picked from commit be5881280f)

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-07-11 17:31:06 +05:30
Deepesh Garg
a76285b349 Merge branch 'version-14' into version-14-hotfix 2023-07-11 16:14:57 +05:30
mergify[bot]
f4f886c7d1 fix: Validate for missing expense account (#36078)
fix: Validate for missing expense account (#36078)

* fix: Validate for missing expense account

* fix: Validate for missing expense account

(cherry picked from commit ce9164ec69)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-11 13:38:13 +05:30
ruthra kumar
7925fc7130 Merge pull request #36077 from frappe/mergify/bp/version-14-hotfix/pr-36076
fix: possible type error on ERR creation (backport #36076)
2023-07-11 11:12:24 +05:30
ruthra kumar
0569899499 fix: possible type error on ERR creation
(cherry picked from commit 176966daab)
2023-07-11 05:08:28 +00:00
mergify[bot]
44f509f989 refactor(Payment Entry): translatable strings (#36017)
* refactor(Payment Entry): translatable strings (#36017)

* refactor(Payment Entry): translatable strings

* fix: German translations

(cherry picked from commit af28f95c60)

# Conflicts:
#	erpnext/accounts/doctype/payment_entry/payment_entry.py
#	erpnext/translations/de.csv

* chore: resolve conflicts

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-07-10 21:01:44 +05:30
mergify[bot]
f3af0b2d2e fix: payment entry voucher_type error (#35779)
fix: payment entry `voucher_type` error (#35779)

* fix: payment entry `voucher_type` error

* chore: linters

(cherry picked from commit 361a357088)

Co-authored-by: Dany Robert <danyrt@wahni.com>
2023-07-10 20:52:36 +05:30
mergify[bot]
59b3277696 fix: also check on_hold (#35910)
fix: also check on_hold (#35910)

(cherry picked from commit 5aa02b8571)

Co-authored-by: RJPvT <48353029+RJPvT@users.noreply.github.com>
2023-07-10 20:34:32 +05:30
Frappe PR Bot
a299092337 chore(release): Bumped to Version 14.29.2
## [14.29.2](https://github.com/frappe/erpnext/compare/v14.29.1...v14.29.2) (2023-07-10)

### Bug Fixes

* Delivery Note return valuation ([8c041eb](8c041eb424))
2023-07-10 14:10:51 +00:00
rohitwaghchaure
db809cb066 Merge pull request #36066 from frappe/mergify/bp/version-14/pr-36065
fix: Delivery Note return valuation (backport #36063) (backport #36065)
2023-07-10 19:39:19 +05:30
mergify[bot]
39e38bf083 fix: Opening balance in presentation currency in Trial Balance report (#36036)
fix: Opening balance in presentation currency in Trial Balance report (#36036)

(cherry picked from commit 4d07e20b05)

# Conflicts:
#	erpnext/accounts/report/trial_balance/trial_balance.py

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-10 19:26:32 +05:30
Rohit Waghchaure
8c041eb424 fix: Delivery Note return valuation
(cherry picked from commit 6a10ae662c)
(cherry picked from commit 296f312e7f)
2023-07-10 12:50:08 +00:00
rohitwaghchaure
7c092a6b5f Merge pull request #36065 from frappe/mergify/bp/version-14-hotfix/pr-36063
fix: Delivery Note return valuation (backport #36063)
2023-07-10 18:18:57 +05:30
mergify[bot]
ebf3c0173e feat: Closing balance for period closing and reporting (#34257)
* feat: Introduce opening entry for reporting

(cherry picked from commit 9739d8b52a)

* feat: Introduce opening entry for reporting

(cherry picked from commit b44a19bd1a)

* fix: Add patches to create accounting dimension in Closing Balance

(cherry picked from commit 36c08d0835)

* feat: Cascade closing balances on PCV submit

(cherry picked from commit c3f39c3f32)

# Conflicts:
#	erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
#	erpnext/patches.txt

* feat: Add views in standard filter

(cherry picked from commit e18336ebe7)

* chore: Rewrite query using query builder

(cherry picked from commit 7fa7d6b5e4)

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

* feat: Add validations against period closing voucher

(cherry picked from commit f92c63fb10)

* fix: Order by issue in aggregation query

(cherry picked from commit 6607c8bd82)

* fix: Add patch to update closing balances

(cherry picked from commit a663df376c)

* fix: Closing balance entries for period closing voucher

(cherry picked from commit 436fc03eda)

* fix: Update patch to generate closing balance entries

(cherry picked from commit 95c9aafda9)

# Conflicts:
#	erpnext/patches.txt

* perf: Apply closing balance in Trial Balance report

(cherry picked from commit e5f603c9d9)

# Conflicts:
#	erpnext/accounts/report/trial_balance/trial_balance.py

* chore: Minor fixes

(cherry picked from commit c089c4156c)

# Conflicts:
#	erpnext/patches.txt

* fix: Aggregation with previous closing balance

(cherry picked from commit 4a2046dfb6)

# Conflicts:
#	erpnext/patches.txt

* test: Add static posting dates to tests

(cherry picked from commit 310f71c313)

* chore: Add index to period closing voucher column

(cherry picked from commit 5dabc98ba5)

* chore: rename Closing Balance to Account Closing Balance

(cherry picked from commit 3249a79f07)

* test: Add test case for closing balance

(cherry picked from commit f0267feca8)

* chore: Use account closing balance in set gl entries

(cherry picked from commit 0157fa15eb)

# Conflicts:
#	erpnext/accounts/report/financial_statements.py

* fix: Update patch

(cherry picked from commit 76775a3e49)

* fix: Account sub query

(cherry picked from commit 7f11373b58)

* chore: Simplify query

(cherry picked from commit 00fe3042b2)

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

* chore: Add missing validations

(cherry picked from commit 0aadb680eb)

* chore: Remove unnecessary list comprehension

(cherry picked from commit 44053db010)

# Conflicts:
#	erpnext/accounts/report/financial_statements.py

* fix: Ignore opening entries if PCV posted

(cherry picked from commit f9397a87ac)

# Conflicts:
#	erpnext/accounts/report/financial_statements.py

* fix: Don't validate if no GL Entry exists

(cherry picked from commit 528ab503f2)

* chore: Fix Typo

(cherry picked from commit 3fd95200da)

* fix: Validation for cancelation

(cherry picked from commit 30eb6c8512)

* fix: Partial trial balance view

(cherry picked from commit b7dcf27b01)

* fix: CS financial statement param

(cherry picked from commit f8cff09129)

* chore: Improve validation message

(cherry picked from commit 8ce1da111e)

* chore: Resolve conflicts

* chore: Resolve conflicts

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-10 18:04:45 +05:30
Rohit Waghchaure
296f312e7f fix: Delivery Note return valuation
(cherry picked from commit 6a10ae662c)
2023-07-10 12:19:37 +00:00
rohitwaghchaure
16943ac248 Merge pull request #36057 from frappe/mergify/bp/version-14-hotfix/pr-36001
fix: accepted warehouse and rejected warehouse can't be same (backport #36001)
2023-07-10 17:48:29 +05:30
mergify[bot]
573183cff5 fix: deferred accounting entries on accounts frozen (#35978)
* fix: deferred accounting entries on accounts frozen (#35978)

* fix: accounts frozen entries in deferred accounting

* test: accounts frozen date in deferred accounting

* fix: reset account settings after running test

* fix: resolve conflicts

* fix: modify expected gle when deferred accounting is disabled through JE

* fix: change posting date when accounts not frozen

(cherry picked from commit 674af15696)

# Conflicts:
#	erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py

* chore: resolve conflicts

* fix: test for deferred accounting

* chore: Linting Issues

---------

Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-10 17:31:17 +05:30
rohitwaghchaure
e55a264e57 fix: conflicts 2023-07-10 17:14:25 +05:30
rohitwaghchaure
79f9785d15 fix: conflicts 2023-07-10 17:13:55 +05:30
rohitwaghchaure
a178e6693c fix: conflicts 2023-07-10 17:13:23 +05:30
rohitwaghchaure
6459c28316 fix: conflicts 2023-07-10 17:12:41 +05:30
mergify[bot]
3e711e888d fix: precision causing outstanding issue on partly paid invoices (#36030)
fix: precision causing outstanding issue on partly paid invoices (#36030)

* fix: precision causing outstanding issue on partly paid invoices

* chore: linters

(cherry picked from commit 5c820ecc20)

Co-authored-by: Dany Robert <danyrt@wahni.com>
2023-07-10 16:49:22 +05:30
mergify[bot]
04b1d459eb fix: labels and translations (#35963)
fix: labels and translations (#35963)

fix: labels and translations
* fix: Vietnamese translation of customer
* fix: Vietnamese translation of bill

(cherry picked from commit 46fe9ac5cd)

Co-authored-by: aioaccount <92444849+aioaccount@users.noreply.github.com>
2023-07-10 16:19:36 +05:30
ruthra kumar
3c563b8cea Merge pull request #35994 from frappe/mergify/bp/version-14-hotfix/pr-35397
fix: incorrect TCS amount while customer has advance payment (backport #35397)
2023-07-10 16:18:47 +05:30
ruthra kumar
4fa3f96121 chore: fix failing test case 2023-07-10 15:48:56 +05:30
ruthra kumar
681d48a7f9 Merge pull request #36053 from frappe/mergify/bp/version-14-hotfix/pr-36051
feat: Provision to auto create Exchange Rate Revaluation (backport #36051)
2023-07-10 15:36:16 +05:30
ruthra kumar
881476b4fb chore: fix merge conflict 2023-07-10 15:09:37 +05:30
Rohit Waghchaure
5d77e3ce05 fix: accepted warehouse and rejected warehouse can't be same
(cherry picked from commit d618aaef32)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
#	erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
#	erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
#	erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
2023-07-10 08:26:13 +00:00
Anand Baburajan
0f9a6ee70a chore: add asset depr posting error in error log (#36052) 2023-07-10 13:45:56 +05:30
ruthra kumar
862c5145c1 refactor: submit and make JV through background job
(cherry picked from commit 4f51c5a433)

# Conflicts:
#	erpnext/setup/doctype/company/company.json
2023-07-10 07:31:19 +00:00
ruthra kumar
e0fbf0c042 refactor: checkbox for enabling auto ERR creation
(cherry picked from commit 6644311c8b)

# Conflicts:
#	erpnext/setup/doctype/company/company.json
2023-07-10 07:31:19 +00:00
mergify[bot]
e443e6c02a fix: Vietnamese translation of "Company" (#35887)
fix: Vietnamese translation of "Company" (#35887)

fix: Vietnamese translation of "Company"
(cherry picked from commit ef7fd7548c)

Co-authored-by: aioaccount <92444849+aioaccount@users.noreply.github.com>
2023-07-09 20:34:26 +05:30
mergify[bot]
b7b864e68c fix: German translations (#35990)
fix: German translations (#35990)

* fix: add missing German translation

* fix: wrong German translation

(cherry picked from commit 353d765140)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-07-09 20:18:22 +05:30
mergify[bot]
29ac533af4 fix:bank reconciliation tool variable issue (#36022)
fix: bank reconciliation tool variable issue (#36022)

fix: bank reconciliation tool variable issue (#36022)
(cherry picked from commit 828e647019)

Co-authored-by: Navin Balaji <54995833+navinbalaji@users.noreply.github.com>
2023-07-09 20:17:54 +05:30
Deepesh Garg
2deb1edec1 Merge branch 'version-14-hotfix' into mergify/bp/version-14-hotfix/pr-35397 2023-07-09 14:12:59 +05:30
Frappe PR Bot
78318964c7 chore(release): Bumped to Version 14.29.1
## [14.29.1](https://github.com/frappe/erpnext/compare/v14.29.0...v14.29.1) (2023-07-05)

### Bug Fixes

* **Payment Entry:** compare rounded amount ([#36011](https://github.com/frappe/erpnext/issues/36011)) ([03e4583](03e458390b))
2023-07-05 16:23:44 +00:00
Deepesh Garg
279f51e636 Merge pull request #36016 from frappe/mergify/bp/version-14/pr-36013
fix(Payment Entry): compare rounded amount (#36011)
2023-07-05 21:47:31 +05:30
mergify[bot]
5102d0c3f7 fix: Share ledger showing cancelled docs (#35993)
fix: Share ledger showing cancelled docs (#35993)

(cherry picked from commit 0a17c78a36)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-05 21:39:38 +05:30
mergify[bot]
03e458390b fix(Payment Entry): compare rounded amount (#36011)
fix(Payment Entry): compare rounded amount (#36011)

(cherry picked from commit 4badac8e9e)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
(cherry picked from commit d80b0aa157)
2023-07-05 16:09:11 +00:00
mergify[bot]
d80b0aa157 fix(Payment Entry): compare rounded amount (#36011)
fix(Payment Entry): compare rounded amount (#36011)

(cherry picked from commit 4badac8e9e)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-07-05 21:38:30 +05:30
Frappe PR Bot
f360bdcbf6 chore(release): Bumped to Version 14.29.0
# [14.29.0](https://github.com/frappe/erpnext/compare/v14.28.1...v14.29.0) (2023-07-05)

### Bug Fixes

* Expense Account filter in Sales Invoice ([#35944](https://github.com/frappe/erpnext/issues/35944)) ([a485e4e](a485e4e802))
* get base grand total while pulling reference details in PE ([2e2c23a](2e2c23aa0f))
* handle loan_repayment's posting_date datetime in bank_clearance_summary report ([#36004](https://github.com/frappe/erpnext/issues/36004)) ([937e1fb](937e1fb024))
* Netherlands - Grootboekschema COA structure ([#35991](https://github.com/frappe/erpnext/issues/35991)) ([13f3ebf](13f3ebf915))
* project filtering based on company in P&L Report ([#35943](https://github.com/frappe/erpnext/issues/35943)) ([8de1d86](8de1d8663f))
* remove debug flag from sql ([646440f](646440fd55))
* reposting has not changed valuation rate ([c0c693d](c0c693d8b0))
* Update no copy for received_qty field ([#35965](https://github.com/frappe/erpnext/issues/35965)) ([c330f47](c330f47680))

### Features

* **accounts:** standardize additional columns implementation for sales/purchase reports ([#36000](https://github.com/frappe/erpnext/issues/36000)) ([47c6d90](47c6d9099b))
* add method for ordered quantity in supplier scorecard (backport [#35930](https://github.com/frappe/erpnext/issues/35930)) ([#35968](https://github.com/frappe/erpnext/issues/35968)) ([a974091](a974091678))
* add voucher-wise balance report logic ([6842902](684290233f))
* allow the partial return of components against SCO (backport [#35935](https://github.com/frappe/erpnext/issues/35935)) ([#35938](https://github.com/frappe/erpnext/issues/35938)) ([6f50ad6](6f50ad685e))
2023-07-05 09:48:12 +00:00
Deepesh Garg
4fa412fe3f Merge pull request #35996 from frappe/version-14-hotfix
chore: release v14
2023-07-05 15:16:47 +05:30
Anand Baburajan
937e1fb024 fix: handle loan_repayment's posting_date datetime in bank_clearance_summary report (#36004) 2023-07-04 19:06:58 +05:30
Sagar Vora
dfd4ef178e Merge pull request #36002 from frappe/mergify/bp/version-14-hotfix/pr-36000 2023-07-04 17:42:51 +05:30
Sagar Vora
47c6d9099b feat(accounts): standardize additional columns implementation for sales/purchase reports (#36000)
(cherry picked from commit 30e4052a76)
2023-07-04 12:12:34 +00:00
mergify[bot]
13f3ebf915 fix: Netherlands - Grootboekschema COA structure (#35991)
fix: Netherlands - Grootboekschema COA structure (#35991)

fix: Netherlands - Grootboekschema coa structure
(cherry picked from commit 2f169575e9)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-04 16:22:17 +05:30
ruthra kumar
2a4bbf34b4 fix: incorrect TCS amount while customer has advance payment (#35397)
* fix: incorrect TCS amount while customer has advance payment

* test: only unallocated advance should for threshold breach validation

(cherry picked from commit dcbd7d5f1f)
2023-07-04 16:21:54 +05:30
ruthra kumar
ae4a8c8788 Merge pull request #35981 from frappe/mergify/bp/version-14-hotfix/pr-35868
fix: incorrect outstanding and total amount in reference table of payment entry (backport #35868)
2023-07-04 08:09:05 +05:30
ruthra kumar
3d1942571d test: test reference details response
(cherry picked from commit 9655d78642)
2023-07-03 15:33:20 +00:00
ruthra kumar
2e2c23aa0f fix: get base grand total while pulling reference details in PE
(cherry picked from commit 9e73af891d)
2023-07-03 15:33:20 +00:00
mergify[bot]
c330f47680 fix: Update no copy for received_qty field (#35965)
* fix: Update no copy for received_qty field (#35965)

(cherry picked from commit 5448859254)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
#	erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json

* chore: resolve conflicts

* chore: resolve conflicts

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-03 16:38:08 +05:30
mergify[bot]
a974091678 feat: add method for ordered quantity in supplier scorecard (backport #35930) (#35968)
feat: add method for ordered quantity in supplier scorecard (#35930)

fix: add method for getting ordered quantity in the supplier scorecard variable.

Co-authored-by: vishnu <vishnuviswambara2002@gmail.com>
(cherry picked from commit e05b33a6c2)

Co-authored-by: Vishnu  VS <Vishnuviswambaran2002@gmail.com>
2023-07-03 10:23:36 +05:30
Frappe PR Bot
87c0417e22 chore(release): Bumped to Version 14.28.1
## [14.28.1](https://github.com/frappe/erpnext/compare/v14.28.0...v14.28.1) (2023-07-02)

### Bug Fixes

* reposting has not changed valuation rate ([cec3cde](cec3cdec66))
2023-07-02 05:49:11 +00:00
rohitwaghchaure
dbae36ece3 Merge pull request #35962 from frappe/mergify/bp/version-14/pr-35955
fix: incorrect reposting causing stock adjustment entry (backport #35955)
2023-07-02 11:17:48 +05:30
Rohit Waghchaure
cec3cdec66 fix: reposting has not changed valuation rate
(cherry picked from commit c0c693d8b0)
2023-07-02 05:18:37 +00:00
rohitwaghchaure
c3eab84e37 Merge pull request #35955 from rohitwaghchaure/fixed-incorrect-valuation-rate
fix: incorrect reposting causing stock adjustment entry
2023-07-02 08:22:16 +05:30
Rohit Waghchaure
c0c693d8b0 fix: reposting has not changed valuation rate 2023-07-01 23:50:27 +05:30
mergify[bot]
a485e4e802 fix: Expense Account filter in Sales Invoice (#35944)
fix: Expense Account filter in Sales Invoice (#35944)

(cherry picked from commit d54f52474a)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-07-01 18:13:46 +05:30
mergify[bot]
8de1d8663f fix: project filtering based on company in P&L Report (#35943)
* fix: project filtering in P&L Report

(cherry picked from commit 904ca746a6)

* fix: show projects with no company value set

(cherry picked from commit ce252a0d45)

* fix: make company field mandatory in project doctype

(cherry picked from commit 84d4888f5f)

---------

Co-authored-by: Gursheen Anand <gursheen@frappe.io>
2023-07-01 10:42:33 +05:30
mergify[bot]
6f50ad685e feat: allow the partial return of components against SCO (backport #35935) (#35938)
* fix: don't update SCO status to closed until full return

(cherry picked from commit 2f6d56dd62)

* fix: reduce return qty while calculating transferred qty

(cherry picked from commit 2a60884abc)

# Conflicts:
#	erpnext/controllers/subcontracting_controller.py

* chore: `conflicts`

---------

Co-authored-by: s-aga-r <sagarsharma.s312@gmail.com>
2023-06-29 22:16:53 +05:30
mergify[bot]
a880bdec5e chore: update translations (#35905)
chore: update translations

chore: update translations
(cherry picked from commit 1d1103f39c)

Co-authored-by: RJPvT <48353029+RJPvT@users.noreply.github.com>
2023-06-29 13:30:29 +05:30
Deepesh Garg
5a9bd3bac6 Merge pull request #35926 from frappe/mergify/bp/version-14-hotfix/pr-35904
feat: add voucher-wise balance report for unequal dr/cr GL entries (backport #35904)
2023-06-29 09:45:22 +05:30
Gursheen Anand
646440fd55 fix: remove debug flag from sql
(cherry picked from commit 6b9f9f9b0e)
2023-06-29 03:38:17 +00:00
Gursheen Anand
684290233f feat: add voucher-wise balance report logic
(cherry picked from commit 5d726ef037)
2023-06-29 03:38:17 +00:00
308 changed files with 10311 additions and 4682 deletions

38
.github/workflows/release_notes.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
# This action:
#
# 1. Generates release notes using github API.
# 2. Strips unnecessary info like chore/style etc from notes.
# 3. Updates release info.
# This action needs to be maintained on all branches that do releases.
name: 'Release Notes'
on:
workflow_dispatch:
inputs:
tag_name:
description: 'Tag of release like v13.0.0'
required: true
type: string
release:
types: [released]
permissions:
contents: read
jobs:
regen-notes:
name: 'Regenerate release notes'
runs-on: ubuntu-latest
steps:
- name: Update notes
run: |
NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' )
RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/tags/$RELEASE_TAG | jq -r '.id')
gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/$RELEASE_ID -f body="$NEW_NOTES"
env:
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }}

View File

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

View File

@@ -4,18 +4,19 @@
"creation": "2020-07-17 11:25:34.593061",
"docstatus": 0,
"doctype": "Dashboard Chart",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"frappe.sys_defaults.fiscal_year\",\"to_fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"erpnext.utils.get_fiscal_year()\",\"to_fiscal_year\":\"erpnext.utils.get_fiscal_year()\"}",
"filters_json": "{\"period\":\"Monthly\",\"budget_against\":\"Cost Center\",\"show_cumulative\":0}",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"modified": "2020-07-22 12:24:49.144210",
"modified": "2023-07-19 13:13:13.307073",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Budget Variance",
"number_of_groups": 0,
"owner": "Administrator",
"report_name": "Budget Variance Report",
"roles": [],
"timeseries": 0,
"type": "Bar",
"use_report_chart": 1,

View File

@@ -4,18 +4,19 @@
"creation": "2020-07-17 11:25:34.448572",
"docstatus": 0,
"doctype": "Dashboard Chart",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"frappe.sys_defaults.fiscal_year\",\"to_fiscal_year\":\"frappe.sys_defaults.fiscal_year\"}",
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_fiscal_year\":\"erpnext.utils.get_fiscal_year()\",\"to_fiscal_year\":\"erpnext.utils.get_fiscal_year()\"}",
"filters_json": "{\"filter_based_on\":\"Fiscal Year\",\"period_start_date\":\"2020-04-01\",\"period_end_date\":\"2021-03-31\",\"periodicity\":\"Yearly\",\"include_default_book_entries\":1}",
"idx": 0,
"is_public": 1,
"is_standard": 1,
"modified": "2020-07-22 12:33:48.888943",
"modified": "2023-07-19 13:08:56.470390",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Profit and Loss",
"number_of_groups": 0,
"owner": "Administrator",
"report_name": "Profit and Loss Statement",
"roles": [],
"timeseries": 0,
"type": "Bar",
"use_report_chart": 1,

View File

@@ -136,7 +136,7 @@ def convert_deferred_revenue_to_income(
send_mail(deferred_process)
def get_booking_dates(doc, item, posting_date=None):
def get_booking_dates(doc, item, posting_date=None, prev_posting_date=None):
if not posting_date:
posting_date = add_days(today(), -1)
@@ -146,39 +146,42 @@ def get_booking_dates(doc, item, posting_date=None):
"deferred_revenue_account" if doc.doctype == "Sales Invoice" else "deferred_expense_account"
)
prev_gl_entry = frappe.db.sql(
"""
select name, posting_date from `tabGL Entry` where company=%s and account=%s and
voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
and is_cancelled = 0
order by posting_date desc limit 1
""",
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
as_dict=True,
)
if not prev_posting_date:
prev_gl_entry = frappe.db.sql(
"""
select name, posting_date from `tabGL Entry` where company=%s and account=%s and
voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
and is_cancelled = 0
order by posting_date desc limit 1
""",
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
as_dict=True,
)
prev_gl_via_je = frappe.db.sql(
"""
SELECT p.name, p.posting_date FROM `tabJournal Entry` p, `tabJournal Entry Account` c
WHERE p.name = c.parent and p.company=%s and c.account=%s
and c.reference_type=%s and c.reference_name=%s
and c.reference_detail_no=%s and c.docstatus < 2 order by posting_date desc limit 1
""",
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
as_dict=True,
)
prev_gl_via_je = frappe.db.sql(
"""
SELECT p.name, p.posting_date FROM `tabJournal Entry` p, `tabJournal Entry Account` c
WHERE p.name = c.parent and p.company=%s and c.account=%s
and c.reference_type=%s and c.reference_name=%s
and c.reference_detail_no=%s and c.docstatus < 2 order by posting_date desc limit 1
""",
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
as_dict=True,
)
if prev_gl_via_je:
if (not prev_gl_entry) or (
prev_gl_entry and prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date
):
prev_gl_entry = prev_gl_via_je
if prev_gl_via_je:
if (not prev_gl_entry) or (
prev_gl_entry and prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date
):
prev_gl_entry = prev_gl_via_je
if prev_gl_entry:
start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
else:
start_date = item.service_start_date
if prev_gl_entry:
start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1))
else:
start_date = item.service_start_date
start_date = getdate(add_days(prev_posting_date, 1))
end_date = get_last_day(start_date)
if end_date >= item.service_end_date:
end_date = item.service_end_date
@@ -338,12 +341,18 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
"enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
)
accounts_frozen_upto = frappe.get_cached_value("Accounts Settings", "None", "acc_frozen_upto")
accounts_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto")
def _book_deferred_revenue_or_expense(
item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on
item,
via_journal_entry,
submit_journal_entry,
book_deferred_entries_based_on,
prev_posting_date=None,
):
start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
start_date, end_date, last_gl_entry = get_booking_dates(
doc, item, posting_date=posting_date, prev_posting_date=prev_posting_date
)
if not (start_date and end_date):
return
@@ -377,9 +386,12 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
if not amount:
return
gl_posting_date = end_date
prev_posting_date = None
# check if books nor frozen till endate:
if accounts_frozen_upto and getdate(end_date) <= getdate(accounts_frozen_upto):
end_date = get_last_day(add_days(accounts_frozen_upto, 1))
gl_posting_date = get_last_day(add_days(accounts_frozen_upto, 1))
prev_posting_date = end_date
if via_journal_entry:
book_revenue_via_journal_entry(
@@ -388,7 +400,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
debit_account,
amount,
base_amount,
end_date,
gl_posting_date,
project,
account_currency,
item.cost_center,
@@ -404,7 +416,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
against,
amount,
base_amount,
end_date,
gl_posting_date,
project,
account_currency,
item.cost_center,
@@ -418,7 +430,11 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
if getdate(end_date) < getdate(posting_date) and not last_gl_entry:
_book_deferred_revenue_or_expense(
item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on
item,
via_journal_entry,
submit_journal_entry,
book_deferred_entries_based_on,
prev_posting_date,
)
via_journal_entry = cint(

View File

@@ -2,75 +2,13 @@
"country_code": "nl",
"name": "Netherlands - Grootboekschema",
"tree": {
"FABRIKAGEREKENINGEN": {
"is_group": 1,
"root_type": "Expense"
},
"FINANCIELE REKENINGEN, KORTLOPENDE VORDERINGEN EN SCHULDEN": {
"Bank": {
"RABO Bank": {
"account_type": "Bank"
},
"account_type": "Bank"
},
"KORTLOPENDE SCHULDEN": {
"Af te dragen Btw-verlegd": {
"account_type": "Tax"
},
"Afdracht loonheffing": {},
"Btw af te dragen hoog": {
"account_type": "Tax"
},
"Btw af te dragen laag": {
"account_type": "Tax"
},
"Btw af te dragen overig": {
"account_type": "Tax"
},
"Btw oude jaren": {
"account_type": "Tax"
},
"Btw te vorderen hoog": {
"account_type": "Tax"
},
"Btw te vorderen laag": {
"account_type": "Tax"
},
"Btw te vorderen overig": {
"account_type": "Tax"
},
"Btw-afdracht": {
"account_type": "Tax"
},
"Crediteuren": {
"account_type": "Payable"
},
"Dividend": {},
"Dividendbelasting": {},
"Energiekosten 1": {},
"Investeringsaftrek": {},
"Loonheffing": {},
"Overige te betalen posten": {},
"Pensioenpremies 1": {},
"Premie WIR": {},
"Rekening-courant inkoopvereniging": {},
"Rente": {},
"Sociale lasten 1": {},
"Stock Recieved niet gefactureerd": {
"account_type": "Stock Received But Not Billed"
},
"Tanti\u00e8mes 1": {},
"Te vorderen Btw-verlegd": {
"account_type": "Tax"
},
"Telefoon/telefax 1": {},
"Termijnen onderh. werk": {},
"Vakantiedagen": {},
"Vakantiegeld 1": {},
"Vakantiezegels": {},
"Vennootschapsbelasting": {},
"Vooruit ontvangen bedr.": {}
},
},
"LIQUIDE MIDDELEN": {
"ABN-AMRO bank": {},
"Bankbetaalkaarten": {},
@@ -91,6 +29,110 @@
},
"account_type": "Cash"
},
"TUSSENREKENINGEN": {
"Betaalwijze cadeaubonnen": {
"account_type": "Cash"
},
"Betaalwijze chipknip": {
"account_type": "Cash"
},
"Betaalwijze contant": {
"account_type": "Cash"
},
"Betaalwijze pin": {
"account_type": "Cash"
},
"Inkopen Nederland hoog": {
"account_type": "Cash"
},
"Inkopen Nederland laag": {
"account_type": "Cash"
},
"Inkopen Nederland onbelast": {
"account_type": "Cash"
},
"Inkopen Nederland overig": {
"account_type": "Cash"
},
"Inkopen Nederland verlegd": {
"account_type": "Cash"
},
"Inkopen binnen EU hoog": {
"account_type": "Cash"
},
"Inkopen binnen EU laag": {
"account_type": "Cash"
},
"Inkopen binnen EU overig": {
"account_type": "Cash"
},
"Inkopen buiten EU hoog": {
"account_type": "Cash"
},
"Inkopen buiten EU laag": {
"account_type": "Cash"
},
"Inkopen buiten EU overig": {
"account_type": "Cash"
},
"Kassa 1": {
"account_type": "Cash"
},
"Kassa 2": {
"account_type": "Cash"
},
"Netto lonen": {
"account_type": "Cash"
},
"Tegenrekening Inkopen": {
"account_type": "Cash"
},
"Tussenrek. autom. betalingen": {
"account_type": "Cash"
},
"Tussenrek. autom. loonbetalingen": {
"account_type": "Cash"
},
"Tussenrek. cadeaubonbetalingen": {
"account_type": "Cash"
},
"Tussenrekening balans": {
"account_type": "Cash"
},
"Tussenrekening chipknip": {
"account_type": "Cash"
},
"Tussenrekening correcties": {
"account_type": "Cash"
},
"Tussenrekening pin": {
"account_type": "Cash"
},
"Vraagposten": {
"account_type": "Cash"
},
"VOORRAAD GRONDSTOFFEN, HULPMATERIALEN EN HANDELSGOEDEREN": {
"Emballage": {},
"Gereed product 1": {},
"Gereed product 2": {},
"Goederen 1": {},
"Goederen 2": {},
"Goederen in consignatie": {},
"Goederen onderweg": {},
"Grondstoffen 1": {},
"Grondstoffen 2": {},
"Halffabrikaten 1": {},
"Halffabrikaten 2": {},
"Hulpstoffen 1": {},
"Hulpstoffen 2": {},
"Kantoorbenodigdheden": {},
"Onderhanden werk": {},
"Verpakkingsmateriaal": {},
"Zegels": {},
"root_type": "Asset"
},
"root_type": "Asset"
},
"VORDERINGEN": {
"Debiteuren": {
"account_type": "Receivable"
@@ -104,278 +146,299 @@
"Voorziening dubieuze debiteuren": {}
},
"root_type": "Asset"
},
"INDIRECTE KOSTEN": {
},
"KORTLOPENDE SCHULDEN": {
"Af te dragen Btw-verlegd": {
"account_type": "Tax"
},
"Afdracht loonheffing": {},
"Btw af te dragen hoog": {
"account_type": "Tax"
},
"Btw af te dragen laag": {
"account_type": "Tax"
},
"Btw af te dragen overig": {
"account_type": "Tax"
},
"Btw oude jaren": {
"account_type": "Tax"
},
"Btw te vorderen hoog": {
"account_type": "Tax"
},
"Btw te vorderen laag": {
"account_type": "Tax"
},
"Btw te vorderen overig": {
"account_type": "Tax"
},
"Btw-afdracht": {
"account_type": "Tax"
},
"Crediteuren": {
"account_type": "Payable"
},
"Dividend": {},
"Dividendbelasting": {},
"Energiekosten 1": {},
"Investeringsaftrek": {},
"Loonheffing": {},
"Overige te betalen posten": {},
"Pensioenpremies 1": {},
"Premie WIR": {},
"Rekening-courant inkoopvereniging": {},
"Rente": {},
"Sociale lasten 1": {},
"Stock Recieved niet gefactureerd": {
"account_type": "Stock Received But Not Billed"
},
"Tanti\u00e8mes 1": {},
"Te vorderen Btw-verlegd": {
"account_type": "Tax"
},
"Telefoon/telefax 1": {},
"Termijnen onderh. werk": {},
"Vakantiedagen": {},
"Vakantiegeld 1": {},
"Vakantiezegels": {},
"Vennootschapsbelasting": {},
"Vooruit ontvangen bedr.": {},
"is_group": 1,
"root_type": "Liability"
},
"FABRIKAGEREKENINGEN": {
"is_group": 1,
"root_type": "Expense"
},
"KOSTENREKENINGEN": {
"AFSCHRIJVINGEN": {
"Aanhangwagens": {},
"Aankoopkosten": {},
"Aanloopkosten": {},
"Auteursrechten": {},
"Bedrijfsgebouwen": {},
"Bedrijfsinventaris": {
"root_type": "Expense",
"INDIRECTE KOSTEN": {
"is_group": 1,
"root_type": "Expense"
},
"KOSTENREKENINGEN": {
"AFSCHRIJVINGEN": {
"Aanhangwagens": {},
"Aankoopkosten": {},
"Aanloopkosten": {},
"Auteursrechten": {},
"Bedrijfsgebouwen": {},
"Bedrijfsinventaris": {
"account_type": "Depreciation"
},
"Drankvergunningen": {},
"Fabrieksinventaris": {
"account_type": "Depreciation"
},
"Gebouwen": {},
"Gereedschappen": {},
"Goodwill": {},
"Grondverbetering": {},
"Heftrucks": {},
"Kantine-inventaris": {},
"Kantoorinventaris": {
"account_type": "Depreciation"
},
"Kantoormachines": {},
"Licenties": {},
"Machines 1": {},
"Magazijninventaris": {},
"Octrooien": {},
"Ontwikkelingskosten": {},
"Pachtersinvestering": {},
"Parkeerplaats": {},
"Personenauto's": {
"account_type": "Depreciation"
},
"Rijwielen en bromfietsen": {},
"Tonnagevergunningen": {},
"Verbouwingen": {},
"Vergunningen": {},
"Voorraadverschillen": {},
"Vrachtauto's": {},
"Winkels": {},
"Woon-winkelhuis": {},
"account_type": "Depreciation"
},
"Drankvergunningen": {},
"Fabrieksinventaris": {
"account_type": "Depreciation"
"ALGEMENE KOSTEN": {
"Accountantskosten": {},
"Advieskosten": {},
"Assuranties 1": {},
"Bankkosten": {},
"Juridische kosten": {},
"Overige algemene kosten": {},
"Toev. Ass. eigen risico": {}
},
"Gebouwen": {},
"Gereedschappen": {},
"Goodwill": {},
"Grondverbetering": {},
"Heftrucks": {},
"Kantine-inventaris": {},
"Kantoorinventaris": {
"account_type": "Depreciation"
"BEDRIJFSKOSTEN": {
"Assuranties 2": {},
"Energie (krachtstroom)": {},
"Gereedschappen 1": {},
"Hulpmaterialen 1": {},
"Huur inventaris": {},
"Huur machines": {},
"Leasing invent.operational": {},
"Leasing mach. operational": {},
"Onderhoud inventaris": {},
"Onderhoud machines": {},
"Ophalen/vervoer afval": {},
"Overige bedrijfskosten": {}
},
"Kantoormachines": {},
"Licenties": {},
"Machines 1": {},
"Magazijninventaris": {},
"Octrooien": {},
"Ontwikkelingskosten": {},
"Pachtersinvestering": {},
"Parkeerplaats": {},
"Personenauto's": {
"account_type": "Depreciation"
"FINANCIERINGSKOSTEN 1": {
"Overige rentebaten": {},
"Overige rentelasten": {},
"Rente bankkrediet": {},
"Rente huurkoopcontracten": {},
"Rente hypotheek": {},
"Rente leasecontracten": {},
"Rente lening o/g": {},
"Rente lening u/g": {}
},
"Rijwielen en bromfietsen": {},
"Tonnagevergunningen": {},
"Verbouwingen": {},
"Vergunningen": {},
"Voorraadverschillen": {},
"Vrachtauto's": {},
"Winkels": {},
"Woon-winkelhuis": {},
"account_type": "Depreciation"
},
"ALGEMENE KOSTEN": {
"Accountantskosten": {},
"Advieskosten": {},
"Assuranties 1": {},
"Bankkosten": {},
"Juridische kosten": {},
"Overige algemene kosten": {},
"Toev. Ass. eigen risico": {}
},
"BEDRIJFSKOSTEN": {
"Assuranties 2": {},
"Energie (krachtstroom)": {},
"Gereedschappen 1": {},
"Hulpmaterialen 1": {},
"Huur inventaris": {},
"Huur machines": {},
"Leasing invent.operational": {},
"Leasing mach. operational": {},
"Onderhoud inventaris": {},
"Onderhoud machines": {},
"Ophalen/vervoer afval": {},
"Overige bedrijfskosten": {}
},
"FINANCIERINGSKOSTEN 1": {
"Overige rentebaten": {},
"Overige rentelasten": {},
"Rente bankkrediet": {},
"Rente huurkoopcontracten": {},
"Rente hypotheek": {},
"Rente leasecontracten": {},
"Rente lening o/g": {},
"Rente lening u/g": {}
},
"HUISVESTINGSKOSTEN": {
"Assurantie onroerend goed": {},
"Belastingen onr. Goed": {},
"Energiekosten": {},
"Groot onderhoud onr. Goed": {},
"Huur": {},
"Huurwaarde woongedeelte": {},
"Onderhoud onroerend goed": {},
"Ontvangen huren": {},
"Overige huisvestingskosten": {},
"Pacht": {},
"Schoonmaakkosten": {},
"Toevoeging egalisatieres. Groot onderhoud": {}
},
"KANTOORKOSTEN": {
"Administratiekosten": {},
"Contributies/abonnementen": {},
"Huur kantoorapparatuur": {},
"Internetaansluiting": {},
"Kantoorbenodigdh./drukw.": {},
"Onderhoud kantoorinvent.": {},
"Overige kantoorkosten": {},
"Porti": {},
"Telefoon/telefax": {}
},
"OVERIGE BATEN EN LASTEN": {
"Betaalde schadevergoed.": {},
"Boekverlies vaste activa": {},
"Boekwinst van vaste activa": {},
"K.O. regeling OB": {},
"Kasverschillen": {},
"Kosten loonbelasting": {},
"Kosten omzetbelasting": {},
"Nadelige koersverschillen": {},
"Naheffing bedrijfsver.": {},
"Ontvangen schadevergoed.": {},
"Overige baten": {},
"Overige lasten": {},
"Voordelige koersverschil.": {}
},
"PERSONEELSKOSTEN": {
"Autokostenvergoeding": {},
"Bedrijfskleding": {},
"Belastingvrije uitkeringen": {},
"Bijzondere beloningen": {},
"Congressen, seminars en symposia": {},
"Gereedschapsgeld": {},
"Geschenken personeel": {},
"Gratificaties": {},
"Inhouding pensioenpremies": {},
"Inhouding sociale lasten": {},
"Kantinekosten": {},
"Lonen en salarissen": {},
"Loonwerk": {},
"Managementvergoedingen": {},
"Opleidingskosten": {},
"Oprenting stamrechtverpl.": {},
"Overhevelingstoeslag": {},
"Overige kostenverg.": {},
"Overige personeelskosten": {},
"Overige uitkeringen": {},
"Pensioenpremies": {},
"Provisie 1": {},
"Reiskosten": {},
"Rijwielvergoeding": {},
"Sociale lasten": {},
"Tanti\u00e8mes": {},
"Thuiswerkers": {},
"Toev. Backservice pens.verpl.": {},
"Toevoeging pensioenverpl.": {},
"Uitkering ziekengeld": {},
"Uitzendkrachten": {},
"Vakantiebonnen": {},
"Vakantiegeld": {},
"Vergoeding studiekosten": {},
"Wervingskosten personeel": {}
},
"VERKOOPKOSTEN": {
"Advertenties": {},
"Afschrijving dubieuze deb.": {},
"Beurskosten": {},
"Etalagekosten": {},
"Exportkosten": {},
"Kascorrecties": {},
"Overige verkoopkosten": {},
"Provisie": {},
"Reclame": {},
"Reis en verblijfkosten": {},
"Relatiegeschenken": {},
"Representatiekosten": {},
"Uitgaande vrachten": {},
"Veilingkosten": {},
"Verpakkingsmateriaal 1": {},
"Websitekosten": {}
},
"VERVOERSKOSTEN": {
"Assuranties auto's": {},
"Brandstoffen": {},
"Leasing auto's": {},
"Onderhoud personenauto's": {},
"Onderhoud vrachtauto's": {},
"Overige vervoerskosten": {},
"Priv\u00e9-gebruik auto's": {},
"Wegenbelasting": {}
},
"root_type": "Expense"
},
"TUSSENREKENINGEN": {
"Betaalwijze cadeaubonnen": {
"account_type": "Cash"
},
"Betaalwijze chipknip": {
"account_type": "Cash"
},
"Betaalwijze contant": {
"account_type": "Cash"
},
"Betaalwijze pin": {
"account_type": "Cash"
},
"Inkopen Nederland hoog": {
"account_type": "Cash"
},
"Inkopen Nederland laag": {
"account_type": "Cash"
},
"Inkopen Nederland onbelast": {
"account_type": "Cash"
},
"Inkopen Nederland overig": {
"account_type": "Cash"
},
"Inkopen Nederland verlegd": {
"account_type": "Cash"
},
"Inkopen binnen EU hoog": {
"account_type": "Cash"
},
"Inkopen binnen EU laag": {
"account_type": "Cash"
},
"Inkopen binnen EU overig": {
"account_type": "Cash"
},
"Inkopen buiten EU hoog": {
"account_type": "Cash"
},
"Inkopen buiten EU laag": {
"account_type": "Cash"
},
"Inkopen buiten EU overig": {
"account_type": "Cash"
},
"Kassa 1": {
"account_type": "Cash"
},
"Kassa 2": {
"account_type": "Cash"
},
"Netto lonen": {
"account_type": "Cash"
},
"Tegenrekening Inkopen": {
"account_type": "Cash"
},
"Tussenrek. autom. betalingen": {
"account_type": "Cash"
},
"Tussenrek. autom. loonbetalingen": {
"account_type": "Cash"
},
"Tussenrek. cadeaubonbetalingen": {
"account_type": "Cash"
},
"Tussenrekening balans": {
"account_type": "Cash"
},
"Tussenrekening chipknip": {
"account_type": "Cash"
},
"Tussenrekening correcties": {
"account_type": "Cash"
},
"Tussenrekening pin": {
"account_type": "Cash"
},
"Vraagposten": {
"account_type": "Cash"
},
"root_type": "Asset"
"HUISVESTINGSKOSTEN": {
"Assurantie onroerend goed": {},
"Belastingen onr. Goed": {},
"Energiekosten": {},
"Groot onderhoud onr. Goed": {},
"Huur": {},
"Huurwaarde woongedeelte": {},
"Onderhoud onroerend goed": {},
"Ontvangen huren": {},
"Overige huisvestingskosten": {},
"Pacht": {},
"Schoonmaakkosten": {},
"Toevoeging egalisatieres. Groot onderhoud": {}
},
"KANTOORKOSTEN": {
"Administratiekosten": {},
"Contributies/abonnementen": {},
"Huur kantoorapparatuur": {},
"Internetaansluiting": {},
"Kantoorbenodigdh./drukw.": {},
"Onderhoud kantoorinvent.": {},
"Overige kantoorkosten": {},
"Porti": {},
"Telefoon/telefax": {}
},
"OVERIGE BATEN EN LASTEN": {
"Betaalde schadevergoed.": {},
"Boekverlies vaste activa": {},
"Boekwinst van vaste activa": {},
"K.O. regeling OB": {},
"Kasverschillen": {},
"Kosten loonbelasting": {},
"Kosten omzetbelasting": {},
"Nadelige koersverschillen": {},
"Naheffing bedrijfsver.": {},
"Ontvangen schadevergoed.": {},
"Overige baten": {},
"Overige lasten": {},
"Voordelige koersverschil.": {}
},
"PERSONEELSKOSTEN": {
"Autokostenvergoeding": {},
"Bedrijfskleding": {},
"Belastingvrije uitkeringen": {},
"Bijzondere beloningen": {},
"Congressen, seminars en symposia": {},
"Gereedschapsgeld": {},
"Geschenken personeel": {},
"Gratificaties": {},
"Inhouding pensioenpremies": {},
"Inhouding sociale lasten": {},
"Kantinekosten": {},
"Lonen en salarissen": {},
"Loonwerk": {},
"Managementvergoedingen": {},
"Opleidingskosten": {},
"Oprenting stamrechtverpl.": {},
"Overhevelingstoeslag": {},
"Overige kostenverg.": {},
"Overige personeelskosten": {},
"Overige uitkeringen": {},
"Pensioenpremies": {},
"Provisie 1": {},
"Reiskosten": {},
"Rijwielvergoeding": {},
"Sociale lasten": {},
"Tanti\u00e8mes": {},
"Thuiswerkers": {},
"Toev. Backservice pens.verpl.": {},
"Toevoeging pensioenverpl.": {},
"Uitkering ziekengeld": {},
"Uitzendkrachten": {},
"Vakantiebonnen": {},
"Vakantiegeld": {},
"Vergoeding studiekosten": {},
"Wervingskosten personeel": {}
},
"VERKOOPKOSTEN": {
"Advertenties": {},
"Afschrijving dubieuze deb.": {},
"Beurskosten": {},
"Etalagekosten": {},
"Exportkosten": {},
"Kascorrecties": {},
"Overige verkoopkosten": {},
"Provisie": {},
"Reclame": {},
"Reis en verblijfkosten": {},
"Relatiegeschenken": {},
"Representatiekosten": {},
"Uitgaande vrachten": {},
"Veilingkosten": {},
"Verpakkingsmateriaal 1": {},
"Websitekosten": {}
},
"VERVOERSKOSTEN": {
"Assuranties auto's": {},
"Brandstoffen": {},
"Leasing auto's": {},
"Onderhoud personenauto's": {},
"Onderhoud vrachtauto's": {},
"Overige vervoerskosten": {},
"Priv\u00e9-gebruik auto's": {},
"Wegenbelasting": {}
},
"VOORRAAD GEREED PRODUCT EN ONDERHANDEN WERK": {
"Betalingskort. crediteuren": {},
"Garantiekosten": {},
"Hulpmaterialen": {},
"Inkomende vrachten": {
"account_type": "Expenses Included In Valuation"
},
"Inkoop import buiten EU hoog": {},
"Inkoop import buiten EU laag": {},
"Inkoop import buiten EU overig": {},
"Inkoopbonussen": {},
"Inkoopkosten": {},
"Inkoopprovisie": {},
"Inkopen BTW verlegd": {},
"Inkopen EU hoog tarief": {},
"Inkopen EU laag tarief": {},
"Inkopen EU overig": {},
"Inkopen hoog": {},
"Inkopen laag": {},
"Inkopen nul": {},
"Inkopen overig": {},
"Invoerkosten": {},
"Kosten inkoopvereniging": {},
"Kostprijs omzet grondstoffen": {
"account_type": "Cost of Goods Sold"
},
"Kostprijs omzet handelsgoederen": {},
"Onttrekking uitgev.garantie": {},
"Priv\u00e9-gebruik goederen": {},
"Stock aanpassing": {
"account_type": "Stock Adjustment"
},
"Tegenrekening inkoop": {},
"Toev. Voorz. incour. grondst.": {},
"Toevoeging garantieverpl.": {},
"Toevoeging voorz. incour. handelsgoed.": {},
"Uitbesteed werk": {},
"Voorz. Incourourant grondst.": {},
"Voorz.incour. handelsgoed.": {},
"root_type": "Expense"
},
"root_type": "Expense"
}
},
"VASTE ACTIVA, EIGEN VERMOGEN, LANGLOPEND VREEMD VERMOGEN EN VOORZIENINGEN": {
"EIGEN VERMOGEN": {
@@ -602,7 +665,7 @@
"account_type": "Equity"
}
},
"root_type": "Asset"
"root_type": "Equity"
},
"VERKOOPRESULTATEN": {
"Diensten fabric. 0% niet-EU": {},
@@ -627,67 +690,6 @@
"Verleende Kredietbep. fabricage": {},
"Verleende Kredietbep. handel": {},
"root_type": "Income"
},
"VOORRAAD GEREED PRODUCT EN ONDERHANDEN WERK": {
"Betalingskort. crediteuren": {},
"Garantiekosten": {},
"Hulpmaterialen": {},
"Inkomende vrachten": {
"account_type": "Expenses Included In Valuation"
},
"Inkoop import buiten EU hoog": {},
"Inkoop import buiten EU laag": {},
"Inkoop import buiten EU overig": {},
"Inkoopbonussen": {},
"Inkoopkosten": {},
"Inkoopprovisie": {},
"Inkopen BTW verlegd": {},
"Inkopen EU hoog tarief": {},
"Inkopen EU laag tarief": {},
"Inkopen EU overig": {},
"Inkopen hoog": {},
"Inkopen laag": {},
"Inkopen nul": {},
"Inkopen overig": {},
"Invoerkosten": {},
"Kosten inkoopvereniging": {},
"Kostprijs omzet grondstoffen": {
"account_type": "Cost of Goods Sold"
},
"Kostprijs omzet handelsgoederen": {},
"Onttrekking uitgev.garantie": {},
"Priv\u00e9-gebruik goederen": {},
"Stock aanpassing": {
"account_type": "Stock Adjustment"
},
"Tegenrekening inkoop": {},
"Toev. Voorz. incour. grondst.": {},
"Toevoeging garantieverpl.": {},
"Toevoeging voorz. incour. handelsgoed.": {},
"Uitbesteed werk": {},
"Voorz. Incourourant grondst.": {},
"Voorz.incour. handelsgoed.": {},
"root_type": "Expense"
},
"VOORRAAD GRONDSTOFFEN, HULPMATERIALEN EN HANDELSGOEDEREN": {
"Emballage": {},
"Gereed product 1": {},
"Gereed product 2": {},
"Goederen 1": {},
"Goederen 2": {},
"Goederen in consignatie": {},
"Goederen onderweg": {},
"Grondstoffen 1": {},
"Grondstoffen 2": {},
"Halffabrikaten 1": {},
"Halffabrikaten 2": {},
"Hulpstoffen 1": {},
"Hulpstoffen 2": {},
"Kantoorbenodigdheden": {},
"Onderhanden werk": {},
"Verpakkingsmateriaal": {},
"Zegels": {},
"root_type": "Asset"
}
}
}

View File

@@ -0,0 +1,8 @@
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
// frappe.ui.form.on("Account Closing Balance", {
// refresh(frm) {
// },
// });

View File

@@ -0,0 +1,164 @@
{
"actions": [],
"creation": "2023-02-21 15:20:59.586811",
"default_view": "List",
"doctype": "DocType",
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
"closing_date",
"account",
"cost_center",
"debit",
"credit",
"account_currency",
"debit_in_account_currency",
"credit_in_account_currency",
"project",
"company",
"finance_book",
"period_closing_voucher",
"is_period_closing_voucher_entry"
],
"fields": [
{
"fieldname": "closing_date",
"fieldtype": "Date",
"in_filter": 1,
"in_list_view": 1,
"label": "Closing Date",
"oldfieldname": "posting_date",
"oldfieldtype": "Date",
"search_index": 1
},
{
"fieldname": "account",
"fieldtype": "Link",
"in_filter": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Account",
"oldfieldname": "account",
"oldfieldtype": "Link",
"options": "Account",
"search_index": 1
},
{
"fieldname": "cost_center",
"fieldtype": "Link",
"in_filter": 1,
"in_list_view": 1,
"label": "Cost Center",
"oldfieldname": "cost_center",
"oldfieldtype": "Link",
"options": "Cost Center"
},
{
"fieldname": "debit",
"fieldtype": "Currency",
"label": "Debit Amount",
"oldfieldname": "debit",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency"
},
{
"fieldname": "credit",
"fieldtype": "Currency",
"label": "Credit Amount",
"oldfieldname": "credit",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency"
},
{
"fieldname": "account_currency",
"fieldtype": "Link",
"label": "Account Currency",
"options": "Currency"
},
{
"fieldname": "debit_in_account_currency",
"fieldtype": "Currency",
"label": "Debit Amount in Account Currency",
"options": "account_currency"
},
{
"fieldname": "credit_in_account_currency",
"fieldtype": "Currency",
"label": "Credit Amount in Account Currency",
"options": "account_currency"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"fieldname": "company",
"fieldtype": "Link",
"in_filter": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Company",
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"search_index": 1
},
{
"fieldname": "finance_book",
"fieldtype": "Link",
"label": "Finance Book",
"options": "Finance Book"
},
{
"fieldname": "period_closing_voucher",
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "Period Closing Voucher",
"options": "Period Closing Voucher",
"search_index": 1
},
{
"default": "0",
"fieldname": "is_period_closing_voucher_entry",
"fieldtype": "Check",
"label": "Is Period Closing Voucher Entry"
}
],
"icon": "fa fa-list",
"in_create": 1,
"links": [],
"modified": "2023-03-06 08:56:36.393237",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account Closing Balance",
"owner": "Administrator",
"permissions": [
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User"
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager"
},
{
"export": 1,
"read": 1,
"report": 1,
"role": "Auditor"
}
],
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}

View File

@@ -0,0 +1,126 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
from frappe.utils import cint, cstr
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
class AccountClosingBalance(Document):
pass
def make_closing_entries(closing_entries, voucher_name, company, closing_date):
accounting_dimensions = get_accounting_dimensions()
previous_closing_entries = get_previous_closing_entries(
company, closing_date, accounting_dimensions
)
combined_entries = closing_entries + previous_closing_entries
merged_entries = aggregate_with_last_account_closing_balance(
combined_entries, accounting_dimensions
)
for key, value in merged_entries.items():
cle = frappe.new_doc("Account Closing Balance")
cle.update(value)
cle.update(value["dimensions"])
cle.update(
{
"period_closing_voucher": voucher_name,
"closing_date": closing_date,
}
)
cle.flags.ignore_permissions = True
cle.submit()
def aggregate_with_last_account_closing_balance(entries, accounting_dimensions):
merged_entries = {}
for entry in entries:
key, key_values = generate_key(entry, accounting_dimensions)
merged_entries.setdefault(
key,
{
"debit": 0,
"credit": 0,
"debit_in_account_currency": 0,
"credit_in_account_currency": 0,
},
)
merged_entries[key]["dimensions"] = key_values
merged_entries[key]["debit"] += entry.get("debit")
merged_entries[key]["credit"] += entry.get("credit")
merged_entries[key]["debit_in_account_currency"] += entry.get("debit_in_account_currency")
merged_entries[key]["credit_in_account_currency"] += entry.get("credit_in_account_currency")
return merged_entries
def generate_key(entry, accounting_dimensions):
key = [
cstr(entry.get("account")),
cstr(entry.get("account_currency")),
cstr(entry.get("cost_center")),
cstr(entry.get("project")),
cstr(entry.get("finance_book")),
cint(entry.get("is_period_closing_voucher_entry")),
]
key_values = {
"company": cstr(entry.get("company")),
"account": cstr(entry.get("account")),
"account_currency": cstr(entry.get("account_currency")),
"cost_center": cstr(entry.get("cost_center")),
"project": cstr(entry.get("project")),
"finance_book": cstr(entry.get("finance_book")),
"is_period_closing_voucher_entry": cint(entry.get("is_period_closing_voucher_entry")),
}
for dimension in accounting_dimensions:
key.append(cstr(entry.get(dimension)))
key_values[dimension] = cstr(entry.get(dimension))
return tuple(key), key_values
def get_previous_closing_entries(company, closing_date, accounting_dimensions):
entries = []
last_period_closing_voucher = frappe.db.get_all(
"Period Closing Voucher",
filters={"docstatus": 1, "company": company, "posting_date": ("<", closing_date)},
fields=["name"],
order_by="posting_date desc",
limit=1,
)
if last_period_closing_voucher:
account_closing_balance = frappe.qb.DocType("Account Closing Balance")
query = frappe.qb.from_(account_closing_balance).select(
account_closing_balance.company,
account_closing_balance.account,
account_closing_balance.account_currency,
account_closing_balance.debit,
account_closing_balance.credit,
account_closing_balance.debit_in_account_currency,
account_closing_balance.credit_in_account_currency,
account_closing_balance.cost_center,
account_closing_balance.project,
account_closing_balance.finance_book,
account_closing_balance.is_period_closing_voucher_entry,
)
for dimension in accounting_dimensions:
query = query.select(account_closing_balance[dimension])
query = query.where(
account_closing_balance.period_closing_voucher == last_period_closing_voucher[0].name
)
entries = query.run(as_dict=1)
return entries

View File

@@ -0,0 +1,9 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
# import frappe
from frappe.tests.utils import FrappeTestCase
class TestAccountClosingBalance(FrappeTestCase):
pass

View File

@@ -15,6 +15,17 @@ frappe.ui.form.on('Accounting Dimension', {
};
});
frm.set_query("offsetting_account", "dimension_defaults", function(doc, cdt, cdn) {
let d = locals[cdt][cdn];
return {
filters: {
company: d.company,
root_type: ["in", ["Asset", "Liability"]],
is_group: 0
}
}
});
if (!frm.is_new()) {
frm.add_custom_button(__('Show {0}', [frm.doc.document_type]), function () {
frappe.set_route("List", frm.doc.document_type);

View File

@@ -39,6 +39,8 @@ class AccountingDimension(Document):
if not self.is_new():
self.validate_document_type_change()
self.validate_dimension_defaults()
def validate_document_type_change(self):
doctype_before_save = frappe.db.get_value("Accounting Dimension", self.name, "document_type")
if doctype_before_save != self.document_type:
@@ -46,6 +48,14 @@ class AccountingDimension(Document):
message += _("Please create a new Accounting Dimension if required.")
frappe.throw(message)
def validate_dimension_defaults(self):
companies = []
for default in self.get("dimension_defaults"):
if default.company not in companies:
companies.append(default.company)
else:
frappe.throw(_("Company {0} is added more than once").format(frappe.bold(default.company)))
def after_insert(self):
if frappe.flags.in_test:
make_dimension_in_accounting_doctypes(doc=self)
@@ -271,6 +281,12 @@ def get_dimensions(with_cost_center_and_project=False):
as_dict=1,
)
if isinstance(with_cost_center_and_project, str):
if with_cost_center_and_project.lower().strip() == "true":
with_cost_center_and_project = True
else:
with_cost_center_and_project = False
if with_cost_center_and_project:
dimension_filters.extend(
[

View File

@@ -8,7 +8,10 @@
"reference_document",
"default_dimension",
"mandatory_for_bs",
"mandatory_for_pl"
"mandatory_for_pl",
"column_break_lqns",
"automatically_post_balancing_accounting_entry",
"offsetting_account"
],
"fields": [
{
@@ -50,6 +53,23 @@
"fieldtype": "Check",
"in_list_view": 1,
"label": "Mandatory For Profit and Loss Account"
},
{
"default": "0",
"fieldname": "automatically_post_balancing_accounting_entry",
"fieldtype": "Check",
"label": "Automatically post balancing accounting entry"
},
{
"fieldname": "offsetting_account",
"fieldtype": "Link",
"label": "Offsetting Account",
"mandatory_depends_on": "eval: doc.automatically_post_balancing_accounting_entry",
"options": "Account"
},
{
"fieldname": "column_break_lqns",
"fieldtype": "Column Break"
}
],
"istable": 1,

View File

@@ -20,5 +20,11 @@ frappe.ui.form.on('Accounting Period', {
}
});
}
frm.set_query("document_type", "closed_documents", () => {
return {
query: "erpnext.controllers.queries.get_doctypes_for_closing",
}
});
}
});

View File

@@ -11,6 +11,10 @@ class OverlapError(frappe.ValidationError):
pass
class ClosedAccountingPeriod(frappe.ValidationError):
pass
class AccountingPeriod(Document):
def validate(self):
self.validate_overlap()
@@ -65,3 +69,42 @@ class AccountingPeriod(Document):
"closed_documents",
{"document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed},
)
def validate_accounting_period_on_doc_save(doc, method=None):
if doc.doctype == "Bank Clearance":
return
elif doc.doctype == "Asset":
if doc.is_existing_asset:
return
else:
date = doc.available_for_use_date
elif doc.doctype == "Asset Repair":
date = doc.completion_date
else:
date = doc.posting_date
ap = frappe.qb.DocType("Accounting Period")
cd = frappe.qb.DocType("Closed Document")
accounting_period = (
frappe.qb.from_(ap)
.from_(cd)
.select(ap.name)
.where(
(ap.name == cd.parent)
& (ap.company == doc.company)
& (cd.closed == 1)
& (cd.document_type == doc.doctype)
& (date >= ap.start_date)
& (date <= ap.end_date)
)
).run(as_dict=1)
if accounting_period:
frappe.throw(
_("You cannot create a {0} within the closed Accounting Period {1}").format(
doc.doctype, frappe.bold(accounting_period[0]["name"])
),
ClosedAccountingPeriod,
)

View File

@@ -6,9 +6,11 @@ import unittest
import frappe
from frappe.utils import add_months, nowdate
from erpnext.accounts.doctype.accounting_period.accounting_period import OverlapError
from erpnext.accounts.doctype.accounting_period.accounting_period import (
ClosedAccountingPeriod,
OverlapError,
)
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.general_ledger import ClosedAccountingPeriod
test_dependencies = ["Item"]
@@ -33,9 +35,9 @@ class TestAccountingPeriod(unittest.TestCase):
ap1.save()
doc = create_sales_invoice(
do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
)
self.assertRaises(ClosedAccountingPeriod, doc.submit)
self.assertRaises(ClosedAccountingPeriod, doc.save)
def tearDown(self):
for d in frappe.get_all("Accounting Period"):

View File

@@ -60,6 +60,7 @@
"closing_settings_tab",
"period_closing_settings_section",
"acc_frozen_upto",
"ignore_account_closing_balance",
"column_break_25",
"frozen_accounts_modifier",
"report_settings_sb",
@@ -408,6 +409,13 @@
"fieldname": "enable_fuzzy_matching",
"fieldtype": "Check",
"label": "Enable Fuzzy Matching"
},
{
"default": "0",
"description": "Financial reports will be generated using GL Entry doctypes (should be enabled if Period Closing Voucher is not posted for all years sequentially or missing) ",
"fieldname": "ignore_account_closing_balance",
"fieldtype": "Check",
"label": "Ignore Account Closing Balance"
}
],
"icon": "icon-cog",
@@ -415,7 +423,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-06-15 18:47:46.430291",
"modified": "2023-07-27 15:05:34.000264",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -14,21 +14,32 @@ from erpnext.stock.utils import check_pending_reposting
class AccountsSettings(Document):
def on_update(self):
frappe.clear_cache()
def validate(self):
frappe.db.set_default(
"add_taxes_from_item_tax_template", self.get("add_taxes_from_item_tax_template", 0)
)
old_doc = self.get_doc_before_save()
clear_cache = False
frappe.db.set_default(
"enable_common_party_accounting", self.get("enable_common_party_accounting", 0)
)
if old_doc.add_taxes_from_item_tax_template != self.add_taxes_from_item_tax_template:
frappe.db.set_default(
"add_taxes_from_item_tax_template", self.get("add_taxes_from_item_tax_template", 0)
)
clear_cache = True
if old_doc.enable_common_party_accounting != self.enable_common_party_accounting:
frappe.db.set_default(
"enable_common_party_accounting", self.get("enable_common_party_accounting", 0)
)
clear_cache = True
self.validate_stale_days()
self.enable_payment_schedule_in_print()
self.validate_pending_reposts()
if old_doc.show_payment_schedule_in_print != self.show_payment_schedule_in_print:
self.enable_payment_schedule_in_print()
if old_doc.acc_frozen_upto != self.acc_frozen_upto:
self.validate_pending_reposts()
if clear_cache:
frappe.clear_cache()
def validate_stale_days(self):
if not self.allow_stale and cint(self.stale_days) <= 0:

View File

@@ -19,7 +19,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
onload: function (frm) {
// Set default filter dates
today = frappe.datetime.get_today()
let today = frappe.datetime.get_today()
frm.doc.bank_statement_from_date = frappe.datetime.add_months(today, -1);
frm.doc.bank_statement_to_date = today;
frm.trigger('bank_account');

View File

@@ -93,6 +93,12 @@ class ExchangeRateRevaluation(Document):
return True
def fetch_and_calculate_accounts_data(self):
accounts = self.get_accounts_data()
if accounts:
for acc in accounts:
self.append("accounts", acc)
@frappe.whitelist()
def get_accounts_data(self):
self.validate_mandatory()
@@ -252,8 +258,8 @@ class ExchangeRateRevaluation(Document):
new_balance_in_base_currency = 0
new_balance_in_account_currency = 0
current_exchange_rate = calculate_exchange_rate_using_last_gle(
company, d.account, d.party_type, d.party
current_exchange_rate = (
calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party) or 0.0
)
gain_loss = new_balance_in_account_currency - (

View File

@@ -8,17 +8,6 @@ frappe.ui.form.on('Fiscal Year', {
frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1));
}
},
refresh: function (frm) {
if (!frm.doc.__islocal && (frm.doc.name != frappe.sys_defaults.fiscal_year)) {
frm.add_custom_button(__("Set as Default"), () => frm.events.set_as_default(frm));
frm.set_intro(__("To set this Fiscal Year as Default, click on 'Set as Default'"));
} else {
frm.set_intro("");
}
},
set_as_default: function(frm) {
return frm.call('set_as_default');
},
year_start_date: function(frm) {
if (!frm.doc.is_short_year) {
let year_end_date =

View File

@@ -4,7 +4,7 @@
import frappe
from dateutil.relativedelta import relativedelta
from frappe import _, msgprint
from frappe import _
from frappe.model.document import Document
from frappe.utils import add_days, add_years, cstr, getdate
@@ -14,22 +14,6 @@ class FiscalYearIncorrectDate(frappe.ValidationError):
class FiscalYear(Document):
@frappe.whitelist()
def set_as_default(self):
frappe.db.set_value("Global Defaults", None, "current_fiscal_year", self.name)
global_defaults = frappe.get_doc("Global Defaults")
global_defaults.check_permission("write")
global_defaults.on_update()
# clear cache
frappe.clear_cache()
msgprint(
_(
"{0} is now the default Fiscal Year. Please refresh your browser for the change to take effect."
).format(self.name)
)
def validate(self):
self.validate_dates()
self.validate_overlap()
@@ -77,13 +61,6 @@ class FiscalYear(Document):
frappe.cache().delete_value("fiscal_years")
def on_trash(self):
global_defaults = frappe.get_doc("Global Defaults")
if global_defaults.current_fiscal_year == self.name:
frappe.throw(
_(
"You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings"
).format(self.name)
)
frappe.cache().delete_value("fiscal_years")
def validate_overlap(self):

View File

@@ -58,7 +58,14 @@ class GLEntry(Document):
validate_balance_type(self.account, adv_adj)
validate_frozen_account(self.account, adv_adj)
if frappe.db.get_value("Account", self.account, "account_type") not in [
if (
self.voucher_type == "Journal Entry"
and frappe.get_cached_value("Journal Entry", self.voucher_no, "voucher_type")
== "Exchange Gain Or Loss"
):
return
if frappe.get_cached_value("Account", self.account, "account_type") not in [
"Receivable",
"Payable",
]:

View File

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

View File

@@ -9,6 +9,7 @@
"engine": "InnoDB",
"field_order": [
"entry_type_and_date",
"is_system_generated",
"title",
"voucher_type",
"naming_series",
@@ -533,13 +534,22 @@
"label": "Process Deferred Accounting",
"options": "Process Deferred Accounting",
"read_only": 1
},
{
"default": "0",
"depends_on": "eval:doc.is_system_generated == 1;",
"fieldname": "is_system_generated",
"fieldtype": "Check",
"label": "Is System Generated",
"no_copy": 1,
"read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 176,
"is_submittable": 1,
"links": [],
"modified": "2023-03-01 14:58:59.286591",
"modified": "2023-08-10 14:32:22.366895",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",

View File

@@ -18,6 +18,7 @@ from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category
)
from erpnext.accounts.party import get_party_account
from erpnext.accounts.utils import (
cancel_exchange_gain_loss_journal,
get_account_currency,
get_balance_on,
get_stock_accounts,
@@ -87,15 +88,16 @@ class JournalEntry(AccountsController):
self.update_invoice_discounting()
def on_cancel(self):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
unlink_ref_doc_from_payment_entries(self)
# References for this Journal are removed on the `on_cancel` event in accounts_controller
super(JournalEntry, self).on_cancel()
self.ignore_linked_doctypes = (
"GL Entry",
"Stock Ledger Entry",
"Payment Ledger Entry",
"Repost Payment Ledger",
"Repost Payment Ledger Items",
"Repost Accounting Ledger",
"Repost Accounting Ledger Items",
)
self.make_gl_entries(1)
self.update_advance_paid()
@@ -396,6 +398,15 @@ class JournalEntry(AccountsController):
d.idx, d.account
)
)
elif (
d.party_type
and frappe.db.get_value("Party Type", d.party_type, "account_type") != account_type
):
frappe.throw(
_("Row {0}: Account {1} and Party Type {2} have different account types").format(
d.idx, d.account, d.party_type
)
)
def check_credit_limit(self):
customers = list(
@@ -478,11 +489,12 @@ class JournalEntry(AccountsController):
)
if not against_entries:
frappe.throw(
_(
"Journal Entry {0} does not have account {1} or already matched against other voucher"
).format(d.reference_name, d.account)
)
if self.voucher_type != "Exchange Gain Or Loss":
frappe.throw(
_(
"Journal Entry {0} does not have account {1} or already matched against other voucher"
).format(d.reference_name, d.account)
)
else:
dr_or_cr = "debit" if d.credit > 0 else "credit"
valid = False
@@ -565,7 +577,9 @@ class JournalEntry(AccountsController):
else:
party_account = against_voucher[1]
if against_voucher[0] != cstr(d.party) or party_account != d.account:
if (
against_voucher[0] != cstr(d.party) or party_account != d.account
) and self.voucher_type != "Exchange Gain Or Loss":
frappe.throw(
_("Row {0}: Party / Account does not match with {1} / {2} in {3} {4}").format(
d.idx,
@@ -747,18 +761,23 @@ class JournalEntry(AccountsController):
)
):
# Modified to include the posting date for which to retreive the exchange rate
d.exchange_rate = get_exchange_rate(
self.posting_date,
d.account,
d.account_currency,
self.company,
d.reference_type,
d.reference_name,
d.debit,
d.credit,
d.exchange_rate,
)
ignore_exchange_rate = False
if self.get("flags") and self.flags.get("ignore_exchange_rate"):
ignore_exchange_rate = True
if not ignore_exchange_rate:
# Modified to include the posting date for which to retreive the exchange rate
d.exchange_rate = get_exchange_rate(
self.posting_date,
d.account,
d.account_currency,
self.company,
d.reference_type,
d.reference_name,
d.debit,
d.credit,
d.exchange_rate,
)
if not d.exchange_rate:
frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
@@ -766,6 +785,9 @@ class JournalEntry(AccountsController):
def create_remarks(self):
r = []
if self.flags.skip_remarks_creation:
return
if self.user_remark:
r.append(_("Note: {0}").format(self.user_remark))
@@ -914,6 +936,8 @@ class JournalEntry(AccountsController):
merge_entries=merge_entries,
update_outstanding=update_outstanding,
)
if cancel:
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))
@frappe.whitelist()
def get_balance(self, difference_account=None):

View File

@@ -5,6 +5,7 @@
import unittest
import frappe
from frappe.tests.utils import change_settings
from frappe.utils import flt, nowdate
from erpnext.accounts.doctype.account.test_account import get_inventory_account
@@ -13,6 +14,7 @@ from erpnext.exceptions import InvalidAccountCurrency
class TestJournalEntry(unittest.TestCase):
@change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
def test_journal_entry_with_against_jv(self):
jv_invoice = frappe.copy_doc(test_records[2])
base_jv = frappe.copy_doc(test_records[0])

View File

@@ -203,7 +203,7 @@
"fieldtype": "Select",
"label": "Reference Type",
"no_copy": 1,
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement"
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement\nPayment Entry"
},
{
"fieldname": "reference_name",
@@ -284,7 +284,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2022-10-26 20:03:10.906259",
"modified": "2023-06-16 14:11:13.507807",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",

View File

@@ -141,7 +141,7 @@ def validate_loyalty_points(ref_doc, points_to_redeem):
)
if points_to_redeem > loyalty_program_details.loyalty_points:
frappe.throw(_("You don't have enought Loyalty Points to redeem"))
frappe.throw(_("You don't have enough Loyalty Points to redeem"))
loyalty_amount = flt(points_to_redeem * loyalty_program_details.conversion_factor)

View File

@@ -7,7 +7,7 @@ cur_frm.cscript.tax_table = "Advance Taxes and Charges";
frappe.ui.form.on('Payment Entry', {
onload: function(frm) {
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', "Repost Payment Ledger"];
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', 'Repost Payment Ledger','Repost Accounting Ledger'];
if(frm.doc.__islocal) {
if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null);
@@ -122,13 +122,10 @@ frappe.ui.form.on('Payment Entry', {
frm.set_query('payment_term', 'references', function(frm, cdt, cdn) {
const child = locals[cdt][cdn];
if (in_list(['Purchase Invoice', 'Sales Invoice'], child.reference_doctype) && child.reference_name) {
let payment_term_list = frappe.get_list('Payment Schedule', {'parent': child.reference_name});
payment_term_list = payment_term_list.map(pt => pt.payment_term);
return {
query: "erpnext.controllers.queries.get_payment_terms_for_references",
filters: {
'name': ['in', payment_term_list]
'reference': child.reference_name
}
}
}
@@ -897,12 +894,12 @@ frappe.ui.form.on('Payment Entry', {
if(frm.doc.payment_type == "Receive"
&& frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions
&& frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) {
unallocated_amount = (frm.doc.base_received_amount + total_deductions + frm.doc.base_total_taxes_and_charges
unallocated_amount = (frm.doc.base_received_amount + total_deductions + flt(frm.doc.base_total_taxes_and_charges)
- frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
} else if (frm.doc.payment_type == "Pay"
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions
&& frm.doc.total_allocated_amount < frm.doc.received_amount + (total_deductions / frm.doc.target_exchange_rate)) {
unallocated_amount = (frm.doc.base_paid_amount + frm.doc.base_total_taxes_and_charges - (total_deductions
unallocated_amount = (frm.doc.base_paid_amount + flt(frm.doc.base_total_taxes_and_charges) - (total_deductions
+ frm.doc.base_total_allocated_amount)) / frm.doc.target_exchange_rate;
}
}

View File

@@ -8,6 +8,7 @@ from functools import reduce
import frappe
from frappe import ValidationError, _, qb, scrub, throw
from frappe.utils import cint, comma_or, flt, getdate, nowdate
from frappe.utils.data import comma_and, fmt_money
import erpnext
from erpnext.accounts.doctype.bank_account.bank_account import (
@@ -23,7 +24,12 @@ from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category
)
from erpnext.accounts.general_ledger import make_gl_entries, process_gl_map
from erpnext.accounts.party import get_party_account
from erpnext.accounts.utils import get_account_currency, get_balance_on, get_outstanding_invoices
from erpnext.accounts.utils import (
cancel_exchange_gain_loss_journal,
get_account_currency,
get_balance_on,
get_outstanding_invoices,
)
from erpnext.controllers.accounts_controller import (
AccountsController,
get_supplier_block_status,
@@ -60,7 +66,7 @@ class PaymentEntry(AccountsController):
def validate(self):
self.setup_party_account_field()
self.set_missing_values()
self.set_missing_ref_details()
self.set_missing_ref_details(force=True)
self.validate_payment_type()
self.validate_party_details()
self.set_exchange_rate()
@@ -99,7 +105,10 @@ class PaymentEntry(AccountsController):
"Payment Ledger Entry",
"Repost Payment Ledger",
"Repost Payment Ledger Items",
"Repost Accounting Ledger",
"Repost Accounting Ledger Items",
)
super(PaymentEntry, self).on_cancel()
self.make_gl_entries(cancel=1)
self.update_outstanding_amounts()
self.update_advance_paid()
@@ -163,53 +172,102 @@ class PaymentEntry(AccountsController):
if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(d.outstanding_amount):
frappe.throw(fail_message.format(d.idx))
def term_based_allocation_enabled_for_reference(
self, reference_doctype: str, reference_name: str
) -> bool:
if (
reference_doctype
and reference_doctype in ["Sales Invoice", "Sales Order", "Purchase Order", "Purchase Invoice"]
and reference_name
):
if template := frappe.db.get_value(reference_doctype, reference_name, "payment_terms_template"):
return frappe.db.get_value(
"Payment Terms Template", template, "allocate_payment_based_on_payment_terms"
)
return False
def validate_allocated_amount_with_latest_data(self):
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,
"get_outstanding_invoices": True,
"get_orders_to_be_billed": True,
}
)
if self.references:
uniq_vouchers = set([(x.reference_doctype, x.reference_name) for x in self.references])
vouchers = [frappe._dict({"voucher_type": x[0], "voucher_no": x[1]}) for x in uniq_vouchers]
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,
"get_outstanding_invoices": True,
"get_orders_to_be_billed": True,
"vouchers": vouchers,
}
)
# 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})
# Group latest_references by (voucher_type, voucher_no)
latest_lookup = {}
for d in latest_references:
d = frappe._dict(d)
latest_lookup.setdefault((d.voucher_type, d.voucher_no), frappe._dict())[d.payment_term] = d
for d in self.get("references"):
latest = latest_lookup.get((d.reference_doctype, d.reference_name))
for idx, d in enumerate(self.get("references"), start=1):
latest = latest_lookup.get((d.reference_doctype, d.reference_name)) or frappe._dict()
# 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' or the 'Get Outstanding Orders' button to get the latest outstanding amounts."
).format(d.reference_doctype, d.reference_name)
)
# If term based allocation is enabled, throw
if (
d.payment_term is None or d.payment_term == ""
) and self.term_based_allocation_enabled_for_reference(
d.reference_doctype, d.reference_name
):
frappe.throw(
_(
"{0} has Payment Term based allocation enabled. Select a Payment Term for Row #{1} in Payment References section"
).format(frappe.bold(d.reference_name), frappe.bold(idx))
)
fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.")
# if no payment template is used by invoice and has a custom term(no `payment_term`), then invoice outstanding will be in 'None' key
latest = latest.get(d.payment_term) or latest.get(None)
if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount):
frappe.throw(fail_message.format(d.idx))
# 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 flt(
d.outstanding_amount, d.precision("outstanding_amount")
) != flt(latest.outstanding_amount, d.precision("outstanding_amount")):
frappe.throw(
_(
"{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts."
).format(_(d.reference_doctype), d.reference_name)
)
# Check for negative outstanding invoices as well
if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(latest.outstanding_amount):
frappe.throw(fail_message.format(d.idx))
fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.")
if (
d.payment_term
and (
(flt(d.allocated_amount)) > 0
and latest.payment_term_outstanding
and (flt(d.allocated_amount) > flt(latest.payment_term_outstanding))
)
and self.term_based_allocation_enabled_for_reference(d.reference_doctype, d.reference_name)
):
frappe.throw(
_(
"Row #{0}: Allocated amount:{1} is greater than outstanding amount:{2} for Payment Term {3}"
).format(
d.idx, d.allocated_amount, latest.payment_term_outstanding, d.payment_term
)
)
if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount):
frappe.throw(fail_message.format(d.idx))
# Check for negative outstanding invoices as well
if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(latest.outstanding_amount):
frappe.throw(fail_message.format(d.idx))
def delink_advance_entry_references(self):
for reference in self.references:
@@ -301,7 +359,7 @@ class PaymentEntry(AccountsController):
def validate_party_details(self):
if self.party:
if not frappe.db.exists(self.party_type, self.party):
frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party))
frappe.throw(_("{0} {1} does not exist").format(_(self.party_type), self.party))
def set_exchange_rate(self, ref_doc=None):
self.set_source_exchange_rate(ref_doc)
@@ -314,7 +372,7 @@ class PaymentEntry(AccountsController):
else:
if ref_doc:
if self.paid_from_account_currency == ref_doc.currency:
self.source_exchange_rate = ref_doc.get("exchange_rate")
self.source_exchange_rate = ref_doc.get("exchange_rate") or ref_doc.get("conversion_rate")
if not self.source_exchange_rate:
self.source_exchange_rate = get_exchange_rate(
@@ -327,7 +385,7 @@ class PaymentEntry(AccountsController):
elif self.paid_to and not self.target_exchange_rate:
if ref_doc:
if self.paid_to_account_currency == ref_doc.currency:
self.target_exchange_rate = ref_doc.get("exchange_rate")
self.target_exchange_rate = ref_doc.get("exchange_rate") or ref_doc.get("conversion_rate")
if not self.target_exchange_rate:
self.target_exchange_rate = get_exchange_rate(
@@ -350,7 +408,9 @@ class PaymentEntry(AccountsController):
continue
if d.reference_doctype not in valid_reference_doctypes:
frappe.throw(
_("Reference Doctype must be one of {0}").format(comma_or(valid_reference_doctypes))
_("Reference Doctype must be one of {0}").format(
comma_or((_(d) for d in valid_reference_doctypes))
)
)
elif d.reference_name:
@@ -363,7 +423,7 @@ class PaymentEntry(AccountsController):
if self.party != ref_doc.get(scrub(self.party_type)):
frappe.throw(
_("{0} {1} is not associated with {2} {3}").format(
d.reference_doctype, d.reference_name, self.party_type, self.party
_(d.reference_doctype), d.reference_name, _(self.party_type), self.party
)
)
else:
@@ -382,18 +442,18 @@ class PaymentEntry(AccountsController):
if ref_party_account != self.party_account:
frappe.throw(
_("{0} {1} is associated with {2}, but Party Account is {3}").format(
d.reference_doctype, d.reference_name, ref_party_account, self.party_account
_(d.reference_doctype), d.reference_name, ref_party_account, self.party_account
)
)
if ref_doc.doctype == "Purchase Invoice" and ref_doc.get("on_hold"):
frappe.throw(
_("{0} {1} is on hold").format(d.reference_doctype, d.reference_name),
title=_("Invalid Invoice"),
_("{0} {1} is on hold").format(_(d.reference_doctype), d.reference_name),
title=_("Invalid Purchase Invoice"),
)
if ref_doc.docstatus != 1:
frappe.throw(_("{0} {1} must be submitted").format(d.reference_doctype, d.reference_name))
frappe.throw(_("{0} {1} must be submitted").format(_(d.reference_doctype), d.reference_name))
def get_valid_reference_doctypes(self):
if self.party_type == "Customer":
@@ -419,14 +479,13 @@ class PaymentEntry(AccountsController):
if outstanding_amount <= 0 and not is_return:
no_oustanding_refs.setdefault(d.reference_doctype, []).append(d)
for k, v in no_oustanding_refs.items():
for reference_doctype, references in no_oustanding_refs.items():
frappe.msgprint(
_(
"{} - {} now has {} as it had no outstanding amount left before submitting the Payment Entry."
"References {0} of type {1} had no outstanding amount left before submitting the Payment Entry. Now they have a negative outstanding amount."
).format(
_(k),
frappe.bold(", ".join(d.reference_name for d in v)),
frappe.bold(_("negative outstanding amount")),
frappe.bold(comma_and([d.reference_name for d in references])),
_(reference_doctype),
)
+ "<br><br>"
+ _("If this is undesirable please cancel the corresponding Payment Entry."),
@@ -461,7 +520,7 @@ class PaymentEntry(AccountsController):
if not valid:
frappe.throw(
_("Against Journal Entry {0} does not have any unmatched {1} entry").format(
d.reference_name, dr_or_cr
d.reference_name, _(dr_or_cr)
)
)
@@ -528,7 +587,7 @@ class PaymentEntry(AccountsController):
if allocated_amount > outstanding:
frappe.throw(
_("Row #{0}: Cannot allocate more than {1} against payment term {2}").format(
idx, outstanding, key[0]
idx, fmt_money(outstanding), key[0]
)
)
@@ -588,7 +647,9 @@ class PaymentEntry(AccountsController):
if not self.apply_tax_withholding_amount:
return
net_total = self.paid_amount
order_amount = self.get_order_net_total()
net_total = flt(order_amount) + flt(self.unallocated_amount)
# Adding args as purchase invoice to get TDS amount
args = frappe._dict(
@@ -633,6 +694,20 @@ class PaymentEntry(AccountsController):
for d in to_remove:
self.remove(d)
def get_order_net_total(self):
if self.party_type == "Supplier":
doctype = "Purchase Order"
else:
doctype = "Sales Order"
docnames = [d.reference_name for d in self.references if d.reference_doctype == doctype]
tax_withholding_net_total = frappe.db.get_value(
doctype, {"name": ["in", docnames]}, ["sum(base_tax_withholding_net_total)"]
)
return tax_withholding_net_total
def apply_taxes(self):
self.initialize_taxes()
self.determine_exclusive_rate()
@@ -719,10 +794,25 @@ class PaymentEntry(AccountsController):
flt(d.allocated_amount) * flt(exchange_rate), self.precision("base_paid_amount")
)
else:
# Use source/target exchange rate, so no difference amount is calculated.
# then update exchange gain/loss amount in reference table
# if there is an exchange gain/loss amount in reference table, submit a JE for that
exchange_rate = 1
if self.payment_type == "Receive":
exchange_rate = self.source_exchange_rate
elif self.payment_type == "Pay":
exchange_rate = self.target_exchange_rate
base_allocated_amount += flt(
flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")
flt(d.allocated_amount) * flt(exchange_rate), self.precision("base_paid_amount")
)
allocated_amount_in_pe_exchange_rate = flt(
flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")
)
d.exchange_gain_loss = base_allocated_amount - allocated_amount_in_pe_exchange_rate
return base_allocated_amount
def set_total_allocated_amount(self):
@@ -832,7 +922,7 @@ class PaymentEntry(AccountsController):
elif paid_amount - additional_charges > total_negative_outstanding:
frappe.throw(
_("Paid Amount cannot be greater than total negative outstanding amount {0}").format(
total_negative_outstanding
fmt_money(total_negative_outstanding)
),
InvalidPaymentEntry,
)
@@ -913,6 +1003,10 @@ class PaymentEntry(AccountsController):
gl_entries = self.build_gl_map()
gl_entries = process_gl_map(gl_entries)
make_gl_entries(gl_entries, cancel=cancel, adv_adj=adv_adj)
if cancel:
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))
else:
self.make_exchange_gain_loss_journal()
def add_party_gl_entries(self, gl_entries):
if self.party_account:
@@ -1308,6 +1402,9 @@ def get_outstanding_reference_documents(args):
if args.get("party_type") == "Member":
return
if not args.get("get_outstanding_invoices") and not args.get("get_orders_to_be_billed"):
args["get_outstanding_invoices"] = True
ple = qb.DocType("Payment Ledger Entry")
common_filter = []
accounting_dimensions_filter = []
@@ -1371,9 +1468,12 @@ def get_outstanding_reference_documents(args):
min_outstanding=args.get("outstanding_amt_greater_than"),
max_outstanding=args.get("outstanding_amt_less_than"),
accounting_dimensions=accounting_dimensions_filter,
vouchers=args.get("vouchers") or None,
)
outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
outstanding_invoices = split_invoices_based_on_payment_terms(
outstanding_invoices, args.get("company")
)
for d in outstanding_invoices:
d["exchange_rate"] = 1
@@ -1425,15 +1525,34 @@ def get_outstanding_reference_documents(args):
_(
"No outstanding {0} found for the {1} {2} which qualify the filters you have specified."
).format(
ref_document_type, _(args.get("party_type")).lower(), frappe.bold(args.get("party"))
_(ref_document_type), _(args.get("party_type")).lower(), frappe.bold(args.get("party"))
)
)
return data
def split_invoices_based_on_payment_terms(outstanding_invoices):
def split_invoices_based_on_payment_terms(outstanding_invoices, company):
invoice_ref_based_on_payment_terms = {}
company_currency = (
frappe.db.get_value("Company", company, "default_currency") if company else None
)
exc_rates = frappe._dict()
for doctype in ["Sales Invoice", "Purchase Invoice"]:
invoices = [x.voucher_no for x in outstanding_invoices if x.voucher_type == doctype]
for x in frappe.db.get_all(
doctype,
filters={"name": ["in", invoices]},
fields=["name", "currency", "conversion_rate", "party_account_currency"],
):
exc_rates[x.name] = frappe._dict(
conversion_rate=x.conversion_rate,
currency=x.currency,
party_account_currency=x.party_account_currency,
company_currency=company_currency,
)
for idx, d in enumerate(outstanding_invoices):
if d.voucher_type in ["Sales Invoice", "Purchase Invoice"]:
payment_term_template = frappe.db.get_value(
@@ -1450,6 +1569,14 @@ def split_invoices_based_on_payment_terms(outstanding_invoices):
for payment_term in payment_schedule:
if payment_term.outstanding > 0.1:
doc_details = exc_rates.get(payment_term.parent, None)
is_multi_currency_acc = (doc_details.currency != doc_details.company_currency) and (
doc_details.party_account_currency != doc_details.company_currency
)
payment_term_outstanding = flt(payment_term.outstanding)
if not is_multi_currency_acc:
payment_term_outstanding = doc_details.conversion_rate * flt(payment_term.outstanding)
invoice_ref_based_on_payment_terms.setdefault(idx, [])
invoice_ref_based_on_payment_terms[idx].append(
frappe._dict(
@@ -1461,6 +1588,10 @@ def split_invoices_based_on_payment_terms(outstanding_invoices):
"posting_date": d.posting_date,
"invoice_amount": flt(d.invoice_amount),
"outstanding_amount": flt(d.outstanding_amount),
"payment_term_outstanding": payment_term_outstanding,
"allocated_amount": payment_term_outstanding
if payment_term_outstanding
else d.outstanding_amount,
"payment_amount": payment_term.payment_amount,
"payment_term": payment_term.payment_term,
}
@@ -1500,60 +1631,59 @@ def get_orders_to_be_billed(
cost_center=None,
filters=None,
):
voucher_type = None
if party_type == "Customer":
voucher_type = "Sales Order"
elif party_type == "Supplier":
voucher_type = "Purchase Order"
elif party_type == "Employee":
voucher_type = None
if not voucher_type:
return []
# Add cost center condition
if voucher_type:
doc = frappe.get_doc({"doctype": voucher_type})
condition = ""
if doc and hasattr(doc, "cost_center") and doc.cost_center:
condition = " and cost_center='%s'" % cost_center
doc = frappe.get_doc({"doctype": voucher_type})
condition = ""
if doc and hasattr(doc, "cost_center") and doc.cost_center:
condition = " and cost_center='%s'" % cost_center
orders = []
if voucher_type:
if party_account_currency == company_currency:
grand_total_field = "base_grand_total"
rounded_total_field = "base_rounded_total"
else:
grand_total_field = "grand_total"
rounded_total_field = "rounded_total"
if party_account_currency == company_currency:
grand_total_field = "base_grand_total"
rounded_total_field = "base_rounded_total"
else:
grand_total_field = "grand_total"
rounded_total_field = "rounded_total"
orders = frappe.db.sql(
"""
select
name as voucher_no,
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
transaction_date as posting_date
from
`tab{voucher_type}`
where
{party_type} = %s
and docstatus = 1
and company = %s
and ifnull(status, "") != "Closed"
and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
and abs(100 - per_billed) > 0.01
{condition}
order by
transaction_date, name
""".format(
**{
"rounded_total_field": rounded_total_field,
"grand_total_field": grand_total_field,
"voucher_type": voucher_type,
"party_type": scrub(party_type),
"condition": condition,
}
),
(party, company),
as_dict=True,
)
orders = frappe.db.sql(
"""
select
name as voucher_no,
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
transaction_date as posting_date
from
`tab{voucher_type}`
where
{party_type} = %s
and docstatus = 1
and company = %s
and ifnull(status, "") != "Closed"
and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
and abs(100 - per_billed) > 0.01
{condition}
order by
transaction_date, name
""".format(
**{
"rounded_total_field": rounded_total_field,
"grand_total_field": grand_total_field,
"voucher_type": voucher_type,
"party_type": scrub(party_type),
"condition": condition,
}
),
(party, company),
as_dict=True,
)
order_list = []
for d in orders:
@@ -1586,6 +1716,8 @@ def get_negative_outstanding_invoices(
cost_center=None,
condition=None,
):
if party_type not in ["Customer", "Supplier"]:
return []
voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice"
supplier_condition = ""
if voucher_type == "Purchase Invoice":
@@ -1634,7 +1766,7 @@ def get_negative_outstanding_invoices(
def get_party_details(company, party_type, party, date, cost_center=None):
bank_account = ""
if not frappe.db.exists(party_type, party):
frappe.throw(_("Invalid {0}: {1}").format(party_type, party))
frappe.throw(_("{0} {1} does not exist").format(_(party_type), party))
party_account = get_party_account(party_type, party, company)
@@ -1735,7 +1867,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
if not total_amount:
if party_account_currency == company_currency:
# for handling cases that don't have multi-currency (base field)
total_amount = ref_doc.get("grand_total") or ref_doc.get("base_grand_total")
total_amount = ref_doc.get("base_grand_total") or ref_doc.get("grand_total")
exchange_rate = 1
else:
total_amount = ref_doc.get("grand_total")
@@ -1777,13 +1909,12 @@ def get_payment_entry(
payment_type=None,
reference_date=None,
):
reference_doc = None
doc = frappe.get_doc(dt, dn)
over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= (
100.0 + over_billing_allowance
):
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
frappe.throw(_("Can only make payment against unbilled {0}").format(_(dt)))
if not party_type:
party_type = set_party_type(dt)
@@ -1918,7 +2049,7 @@ def get_payment_entry(
update_accounting_dimensions(pe, doc)
if party_account and bank:
pe.set_exchange_rate(ref_doc=reference_doc)
pe.set_exchange_rate(ref_doc=doc)
pe.set_amounts()
if discount_amount:
@@ -2234,6 +2365,7 @@ def get_reference_as_per_payment_terms(
"due_date": doc.get("due_date"),
"total_amount": grand_total,
"outstanding_amount": outstanding_amount,
"payment_term_outstanding": payment_term_outstanding,
"payment_term": payment_term.payment_term,
"allocated_amount": payment_term_outstanding,
}

View File

@@ -11,6 +11,7 @@ from frappe.utils import flt, nowdate
from erpnext.accounts.doctype.payment_entry.payment_entry import (
InvalidPaymentEntry,
get_payment_entry,
get_reference_details,
)
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import (
make_purchase_invoice,
@@ -30,6 +31,16 @@ class TestPaymentEntry(FrappeTestCase):
def tearDown(self):
frappe.db.rollback()
def get_journals_for(self, voucher_type: str, voucher_no: str) -> list:
journals = []
if voucher_type and voucher_no:
journals = frappe.db.get_all(
"Journal Entry Account",
filters={"reference_type": voucher_type, "reference_name": voucher_no, "docstatus": 1},
fields=["parent"],
)
return journals
def test_payment_entry_against_order(self):
so = make_sales_order()
pe = get_payment_entry("Sales Order", so.name, bank_account="_Test Cash - _TC")
@@ -590,21 +601,15 @@ class TestPaymentEntry(FrappeTestCase):
pe.target_exchange_rate = 45.263
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
pe.append(
"deductions",
{
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": 94.80,
},
)
pe.save()
self.assertEqual(flt(pe.difference_amount, 2), 0.0)
self.assertEqual(flt(pe.unallocated_amount, 2), 0.0)
# the exchange gain/loss amount is captured in reference table and a separate Journal will be submitted for them
# payment entry will not be generating difference amount
self.assertEqual(flt(pe.references[0].exchange_gain_loss, 2), -94.74)
def test_payment_entry_retrieves_last_exchange_rate(self):
from erpnext.setup.doctype.currency_exchange.test_currency_exchange import (
save_new_records,
@@ -791,33 +796,28 @@ class TestPaymentEntry(FrappeTestCase):
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
pe.source_exchange_rate = 55
pe.append(
"deductions",
{
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": -500,
},
)
pe.save()
self.assertEqual(pe.unallocated_amount, 0)
self.assertEqual(pe.difference_amount, 0)
self.assertEqual(pe.references[0].exchange_gain_loss, 500)
pe.submit()
expected_gle = dict(
(d[0], d)
for d in [
["_Test Receivable USD - _TC", 0, 5000, si.name],
["_Test Receivable USD - _TC", 0, 5500, si.name],
["_Test Bank USD - _TC", 5500, 0, None],
["_Test Exchange Gain/Loss - _TC", 0, 500, None],
]
)
self.validate_gl_entries(pe.name, expected_gle)
# Exchange gain/loss should have been posted through a journal
exc_je_for_si = self.get_journals_for(si.doctype, si.name)
exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name)
self.assertEqual(exc_je_for_si, exc_je_for_pe)
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 0)
@@ -1037,6 +1037,170 @@ class TestPaymentEntry(FrappeTestCase):
self.assertRaises(frappe.ValidationError, pe_draft.submit)
def test_details_update_on_reference_table(self):
so = make_sales_order(
customer="_Test Customer USD", currency="USD", qty=1, rate=100, do_not_submit=True
)
so.conversion_rate = 50
so.submit()
pe = get_payment_entry("Sales Order", so.name)
pe.references.clear()
pe.paid_from = "Debtors - _TC"
pe.paid_from_account_currency = "INR"
pe.source_exchange_rate = 50
pe.save()
ref_details = get_reference_details(so.doctype, so.name, pe.paid_from_account_currency)
expected_response = {
"total_amount": 5000.0,
"outstanding_amount": 5000.0,
"exchange_rate": 1.0,
"due_date": None,
"bill_no": None,
}
self.assertDictEqual(ref_details, expected_response)
@change_settings(
"Accounts Settings",
{
"unlink_payment_on_cancellation_of_invoice": 1,
"delete_linked_ledger_entries": 1,
"allow_multi_currency_invoices_against_single_party_account": 1,
},
)
def test_overallocation_validation_on_payment_terms(self):
"""
Validate Allocation on Payment Entry based on Payment Schedule. Upon overallocation, validation error must be thrown.
"""
customer = create_customer()
create_payment_terms_template()
# Validate allocation on base/company currency
si1 = create_sales_invoice(do_not_save=1, qty=1, rate=200)
si1.payment_terms_template = "Test Receivable Template"
si1.save().submit()
si1.reload()
pe = get_payment_entry(si1.doctype, si1.name).save()
# Allocated amount should be according to the payment schedule
for idx, schedule in enumerate(si1.payment_schedule):
with self.subTest(idx=idx):
self.assertEqual(flt(schedule.payment_amount), flt(pe.references[idx].allocated_amount))
pe.save()
# Overallocation validation should trigger
pe.paid_amount = 400
pe.references[0].allocated_amount = 200
pe.references[1].allocated_amount = 200
self.assertRaises(frappe.ValidationError, pe.save)
pe.delete()
si1.cancel()
si1.delete()
# Validate allocation on foreign currency
si2 = create_sales_invoice(
customer="_Test Customer USD",
debit_to="_Test Receivable USD - _TC",
currency="USD",
conversion_rate=80,
do_not_save=1,
)
si2.payment_terms_template = "Test Receivable Template"
si2.save().submit()
si2.reload()
pe = get_payment_entry(si2.doctype, si2.name).save()
# Allocated amount should be according to the payment schedule
for idx, schedule in enumerate(si2.payment_schedule):
with self.subTest(idx=idx):
self.assertEqual(flt(schedule.payment_amount), flt(pe.references[idx].allocated_amount))
pe.save()
# Overallocation validation should trigger
pe.paid_amount = 200
pe.references[0].allocated_amount = 100
pe.references[1].allocated_amount = 100
self.assertRaises(frappe.ValidationError, pe.save)
pe.delete()
si2.cancel()
si2.delete()
# Validate allocation in base/company currency on a foreign currency document
# when invoice is made is foreign currency, but posted to base/company currency debtors account
si3 = create_sales_invoice(
customer=customer,
currency="USD",
conversion_rate=80,
do_not_save=1,
)
si3.payment_terms_template = "Test Receivable Template"
si3.save().submit()
si3.reload()
pe = get_payment_entry(si3.doctype, si3.name).save()
# Allocated amount should be equal to payment term outstanding
self.assertEqual(len(pe.references), 2)
for idx, ref in enumerate(pe.references):
with self.subTest(idx=idx):
self.assertEqual(ref.payment_term_outstanding, ref.allocated_amount)
pe.save()
# Overallocation validation should trigger
pe.paid_amount = 16000
pe.references[0].allocated_amount = 8000
pe.references[1].allocated_amount = 8000
self.assertRaises(frappe.ValidationError, pe.save)
pe.delete()
si3.cancel()
si3.delete()
@change_settings(
"Accounts Settings",
{
"unlink_payment_on_cancellation_of_invoice": 1,
"delete_linked_ledger_entries": 1,
"allow_multi_currency_invoices_against_single_party_account": 1,
},
)
def test_overallocation_validation_shouldnt_misfire(self):
"""
Overallocation validation shouldn't fire for Template without "Allocate Payment based on Payment Terms" enabled
"""
customer = create_customer()
create_payment_terms_template()
template = frappe.get_doc("Payment Terms Template", "Test Receivable Template")
template.allocate_payment_based_on_payment_terms = 0
template.save()
# Validate allocation on base/company currency
si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
si.payment_terms_template = "Test Receivable Template"
si.save().submit()
si.reload()
pe = get_payment_entry(si.doctype, si.name).save()
# There will no term based allocation
self.assertEqual(len(pe.references), 1)
self.assertEqual(pe.references[0].payment_term, None)
self.assertEqual(flt(pe.references[0].allocated_amount), flt(si.grand_total))
pe.save()
# specify a term
pe.references[0].payment_term = template.terms[0].payment_term
# no validation error should be thrown
pe.save()
pe.paid_amount = si.grand_total + 1
pe.references[0].allocated_amount = si.grand_total + 1
self.assertRaises(frappe.ValidationError, pe.save)
template = frappe.get_doc("Payment Terms Template", "Test Receivable Template")
template.allocate_payment_based_on_payment_terms = 1
template.save()
def create_payment_entry(**args):
payment_entry = frappe.new_doc("Payment Entry")
@@ -1126,3 +1290,17 @@ def create_payment_terms_template_with_discount(
def create_payment_term(name):
if not frappe.db.exists("Payment Term", name):
frappe.get_doc({"doctype": "Payment Term", "payment_term_name": name}).insert()
def create_customer(name="_Test Customer 2 USD", currency="USD"):
customer = None
if frappe.db.exists("Customer", name):
customer = name
else:
customer = frappe.new_doc("Customer")
customer.customer_name = name
customer.default_currency = currency
customer.type = "Individual"
customer.save()
customer = customer.name
return customer

View File

@@ -6,7 +6,7 @@ import frappe
from frappe import _, msgprint, qb
from frappe.model.document import Document
from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt, get_link_to_form, getdate, nowdate, today
from frappe.utils import flt, fmt_money, get_link_to_form, getdate, nowdate, today
import erpnext
from erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation import (
@@ -14,6 +14,7 @@ from erpnext.accounts.doctype.process_payment_reconciliation.process_payment_rec
)
from erpnext.accounts.utils import (
QueryPaymentLedger,
create_gain_loss_journal,
get_outstanding_invoices,
reconcile_against_document,
)
@@ -260,6 +261,11 @@ class PaymentReconciliation(Document):
def calculate_difference_on_allocation_change(self, payment_entry, invoice, allocated_amount):
invoice_exchange_map = self.get_invoice_exchange_map(invoice, payment_entry)
invoice[0]["exchange_rate"] = invoice_exchange_map.get(invoice[0].get("invoice_number"))
if payment_entry[0].get("reference_type") in ["Sales Invoice", "Purchase Invoice"]:
payment_entry[0]["exchange_rate"] = invoice_exchange_map.get(
payment_entry[0].get("reference_name")
)
new_difference_amount = self.get_difference_amount(
payment_entry[0], invoice[0], allocated_amount
)
@@ -347,12 +353,6 @@ class PaymentReconciliation(Document):
payment_details = self.get_payment_details(row, dr_or_cr)
reconciled_entry.append(payment_details)
if payment_details.difference_amount and row.reference_type not in [
"Sales Invoice",
"Purchase Invoice",
]:
self.make_difference_entry(payment_details)
if entry_list:
reconcile_against_document(entry_list, skip_ref_details_update_for_pe)
@@ -385,59 +385,6 @@ class PaymentReconciliation(Document):
self.get_unreconciled_entries()
def make_difference_entry(self, row):
journal_entry = frappe.new_doc("Journal Entry")
journal_entry.voucher_type = "Exchange Gain Or Loss"
journal_entry.company = self.company
journal_entry.posting_date = nowdate()
journal_entry.multi_currency = 1
party_account_currency = frappe.get_cached_value(
"Account", self.receivable_payable_account, "account_currency"
)
difference_account_currency = frappe.get_cached_value(
"Account", row.difference_account, "account_currency"
)
# Account Currency has balance
dr_or_cr = "debit" if self.party_type == "Customer" else "credit"
reverse_dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
journal_account = frappe._dict(
{
"account": self.receivable_payable_account,
"party_type": self.party_type,
"party": self.party,
"account_currency": party_account_currency,
"exchange_rate": 0,
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": row.against_voucher_type,
"reference_name": row.against_voucher,
dr_or_cr: flt(row.difference_amount),
dr_or_cr + "_in_account_currency": 0,
}
)
journal_entry.append("accounts", journal_account)
journal_account = frappe._dict(
{
"account": row.difference_account,
"account_currency": difference_account_currency,
"exchange_rate": 1,
"cost_center": erpnext.get_default_cost_center(self.company),
reverse_dr_or_cr + "_in_account_currency": flt(row.difference_amount),
reverse_dr_or_cr: flt(row.difference_amount),
}
)
journal_entry.append("accounts", journal_account)
journal_entry.save()
journal_entry.submit()
return journal_entry
def get_payment_details(self, row, dr_or_cr):
return frappe._dict(
{
@@ -603,16 +550,6 @@ class PaymentReconciliation(Document):
def reconcile_dr_cr_note(dr_cr_notes, company):
def get_difference_row(inv):
if inv.difference_amount != 0 and inv.difference_account:
difference_row = {
"account": inv.difference_account,
inv.dr_or_cr: abs(inv.difference_amount) if inv.difference_amount > 0 else 0,
reconcile_dr_or_cr: abs(inv.difference_amount) if inv.difference_amount < 0 else 0,
"cost_center": erpnext.get_default_cost_center(company),
}
return difference_row
for inv in dr_cr_notes:
voucher_type = "Credit Note" if inv.voucher_type == "Sales Invoice" else "Debit Note"
@@ -640,6 +577,8 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
"reference_type": inv.against_voucher_type,
"reference_name": inv.against_voucher,
"cost_center": erpnext.get_default_cost_center(company),
"user_remark": f"{fmt_money(flt(inv.allocated_amount), currency=company_currency)} against {inv.against_voucher}",
"exchange_rate": inv.exchange_rate,
},
{
"account": inv.account,
@@ -653,13 +592,42 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
"reference_type": inv.voucher_type,
"reference_name": inv.voucher_no,
"cost_center": erpnext.get_default_cost_center(company),
"user_remark": f"{fmt_money(flt(inv.allocated_amount), currency=company_currency)} from {inv.voucher_no}",
"exchange_rate": inv.exchange_rate,
},
],
}
)
if difference_entry := get_difference_row(inv):
jv.append("accounts", difference_entry)
jv.flags.ignore_mandatory = True
jv.flags.skip_remarks_creation = True
jv.flags.ignore_exchange_rate = True
jv.is_system_generated = True
jv.remark = None
jv.submit()
if inv.difference_amount != 0:
# make gain/loss journal
if inv.party_type == "Customer":
dr_or_cr = "credit" if inv.difference_amount < 0 else "debit"
else:
dr_or_cr = "debit" if inv.difference_amount < 0 else "credit"
reverse_dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
create_gain_loss_journal(
company,
inv.party_type,
inv.party,
inv.account,
inv.difference_account,
inv.difference_amount,
dr_or_cr,
reverse_dr_or_cr,
inv.voucher_type,
inv.voucher_no,
None,
inv.against_voucher_type,
inv.against_voucher,
None,
)

View File

@@ -686,14 +686,24 @@ class TestPaymentReconciliation(FrappeTestCase):
# Check if difference journal entry gets generated for difference amount after reconciliation
pr.reconcile()
total_debit_amount = frappe.db.get_all(
total_credit_amount = frappe.db.get_all(
"Journal Entry Account",
{"account": self.debtors_eur, "docstatus": 1, "reference_name": si.name},
"sum(debit) as amount",
"sum(credit) as amount",
group_by="reference_name",
)[0].amount
self.assertEqual(flt(total_debit_amount, 2), -500)
# total credit includes the exchange gain/loss amount
self.assertEqual(flt(total_credit_amount, 2), 8500)
jea_parent = frappe.db.get_all(
"Journal Entry Account",
filters={"account": self.debtors_eur, "docstatus": 1, "reference_name": si.name, "credit": 500},
fields=["parent"],
)[0]
self.assertEqual(
frappe.db.get_value("Journal Entry", jea_parent.parent, "voucher_type"), "Exchange Gain Or Loss"
)
def test_difference_amount_via_payment_entry(self):
# Make Sale Invoice

View File

@@ -144,8 +144,7 @@ class TestPaymentRequest(unittest.TestCase):
(d[0], d)
for d in [
["_Test Receivable USD - _TC", 0, 5000, si_usd.name],
[pr.payment_account, 6290.0, 0, None],
["_Test Exchange Gain/Loss - _TC", 0, 1290, None],
[pr.payment_account, 5000.0, 0, None],
]
)

View File

@@ -4,12 +4,13 @@
import frappe
from frappe import _
from frappe.utils import flt
from frappe.query_builder.functions import Sum
from frappe.utils import add_days, flt
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
from erpnext.accounts.utils import get_account_currency
from erpnext.accounts.utils import get_account_currency, get_fiscal_year, validate_fiscal_year
from erpnext.controllers.accounts_controller import AccountsController
@@ -20,9 +21,17 @@ class PeriodClosingVoucher(AccountsController):
def on_submit(self):
self.db_set("gle_processing_status", "In Progress")
self.make_gl_entries()
get_opening_entries = False
if not frappe.db.exists(
"Period Closing Voucher", {"company": self.company, "docstatus": 1, "name": ("!=", self.name)}
):
get_opening_entries = True
self.make_gl_entries(get_opening_entries=get_opening_entries)
def on_cancel(self):
self.validate_future_closing_vouchers()
self.db_set("gle_processing_status", "In Progress")
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
gle_count = frappe.db.count(
@@ -43,8 +52,27 @@ class PeriodClosingVoucher(AccountsController):
else:
make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name)
self.delete_closing_entries()
def validate_future_closing_vouchers(self):
if frappe.db.exists(
"Period Closing Voucher",
{"posting_date": (">", self.posting_date), "docstatus": 1, "company": self.company},
):
frappe.throw(
_(
"You can not cancel this Period Closing Voucher, please cancel the future Period Closing Vouchers first"
)
)
def delete_closing_entries(self):
closing_balance = frappe.qb.DocType("Account Closing Balance")
frappe.qb.from_(closing_balance).delete().where(
closing_balance.period_closing_voucher == self.name
).run()
def validate_account_head(self):
closing_account_type = frappe.db.get_value("Account", self.closing_account_head, "root_type")
closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type")
if closing_account_type not in ["Liability", "Equity"]:
frappe.throw(
@@ -57,8 +85,6 @@ class PeriodClosingVoucher(AccountsController):
frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency))
def validate_posting_date(self):
from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year
validate_fiscal_year(
self.posting_date, self.fiscal_year, self.company, label=_("Posting Date"), doc=self
)
@@ -67,6 +93,8 @@ class PeriodClosingVoucher(AccountsController):
self.posting_date, self.fiscal_year, company=self.company
)[1]
self.check_if_previous_year_closed()
pce = frappe.db.sql(
"""select name from `tabPeriod Closing Voucher`
where posting_date > %s and fiscal_year = %s and docstatus = 1 and company = %s""",
@@ -79,28 +107,65 @@ class PeriodClosingVoucher(AccountsController):
)
)
def make_gl_entries(self):
def check_if_previous_year_closed(self):
last_year_closing = add_days(self.year_start_date, -1)
previous_fiscal_year = get_fiscal_year(last_year_closing, company=self.company, boolean=True)
if previous_fiscal_year and not frappe.db.exists(
"GL Entry", {"posting_date": ("<=", last_year_closing), "company": self.company}
):
return
if previous_fiscal_year and not frappe.db.exists(
"Period Closing Voucher",
{"posting_date": ("<=", last_year_closing), "docstatus": 1, "company": self.company},
):
frappe.throw(_("Previous Year is not closed, please close it first"))
def make_gl_entries(self, get_opening_entries=False):
gl_entries = self.get_gl_entries()
if gl_entries:
if len(gl_entries) > 5000:
frappe.enqueue(process_gl_entries, gl_entries=gl_entries, queue="long")
frappe.msgprint(
_("The GL Entries will be processed in the background, it can take a few minutes."),
alert=True,
)
else:
process_gl_entries(gl_entries)
closing_entries = self.get_grouped_gl_entries(get_opening_entries=get_opening_entries)
if len(gl_entries) > 5000:
frappe.enqueue(
process_gl_entries,
gl_entries=gl_entries,
closing_entries=closing_entries,
voucher_name=self.name,
company=self.company,
closing_date=self.posting_date,
queue="long",
)
frappe.msgprint(
_("The GL Entries will be processed in the background, it can take a few minutes."),
alert=True,
)
else:
process_gl_entries(gl_entries, closing_entries, self.name, self.company, self.posting_date)
def get_grouped_gl_entries(self, get_opening_entries=False):
closing_entries = []
for acc in self.get_balances_based_on_dimensions(
group_by_account=True, for_aggregation=True, get_opening_entries=get_opening_entries
):
closing_entries.append(self.get_closing_entries(acc))
return closing_entries
def get_gl_entries(self):
gl_entries = []
# pl account
for acc in self.get_pl_balances_based_on_dimensions(group_by_account=True):
for acc in self.get_balances_based_on_dimensions(
group_by_account=True, report_type="Profit and Loss"
):
if flt(acc.bal_in_company_currency):
gl_entries.append(self.get_gle_for_pl_account(acc))
# closing liability account
for acc in self.get_pl_balances_based_on_dimensions(group_by_account=False):
for acc in self.get_balances_based_on_dimensions(
group_by_account=False, report_type="Profit and Loss"
):
if flt(acc.bal_in_company_currency):
gl_entries.append(self.get_gle_for_closing_account(acc))
@@ -109,6 +174,8 @@ class PeriodClosingVoucher(AccountsController):
def get_gle_for_pl_account(self, acc):
gl_entry = self.get_gl_dict(
{
"company": self.company,
"closing_date": self.posting_date,
"account": acc.account,
"cost_center": acc.cost_center,
"finance_book": acc.finance_book,
@@ -121,6 +188,7 @@ class PeriodClosingVoucher(AccountsController):
if flt(acc.bal_in_account_currency) > 0
else 0,
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0,
"is_period_closing_voucher_entry": 1,
},
item=acc,
)
@@ -130,6 +198,8 @@ class PeriodClosingVoucher(AccountsController):
def get_gle_for_closing_account(self, acc):
gl_entry = self.get_gl_dict(
{
"company": self.company,
"closing_date": self.posting_date,
"account": self.closing_account_head,
"cost_center": acc.cost_center,
"finance_book": acc.finance_book,
@@ -142,12 +212,36 @@ class PeriodClosingVoucher(AccountsController):
if flt(acc.bal_in_account_currency) < 0
else 0,
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0,
"is_period_closing_voucher_entry": 1,
},
item=acc,
)
self.update_default_dimensions(gl_entry, acc)
return gl_entry
def get_closing_entries(self, acc):
closing_entry = self.get_gl_dict(
{
"company": self.company,
"closing_date": self.posting_date,
"period_closing_voucher": self.name,
"account": acc.account,
"cost_center": acc.cost_center,
"finance_book": acc.finance_book,
"account_currency": acc.account_currency,
"debit_in_account_currency": flt(acc.debit_in_account_currency),
"debit": flt(acc.debit),
"credit_in_account_currency": flt(acc.credit_in_account_currency),
"credit": flt(acc.credit),
},
item=acc,
)
for dimension in self.accounting_dimensions:
closing_entry.update({dimension: acc.get(dimension)})
return closing_entry
def update_default_dimensions(self, gl_entry, acc):
if not self.accounting_dimensions:
self.accounting_dimensions = get_accounting_dimensions()
@@ -155,53 +249,95 @@ class PeriodClosingVoucher(AccountsController):
for dimension in self.accounting_dimensions:
gl_entry.update({dimension: acc.get(dimension)})
def get_pl_balances_based_on_dimensions(self, group_by_account=False):
def get_balances_based_on_dimensions(
self, group_by_account=False, report_type=None, for_aggregation=False, get_opening_entries=False
):
"""Get balance for dimension-wise pl accounts"""
dimension_fields = ["t1.cost_center", "t1.finance_book"]
qb_dimension_fields = ["cost_center", "finance_book", "project"]
self.accounting_dimensions = get_accounting_dimensions()
for dimension in self.accounting_dimensions:
dimension_fields.append("t1.{0}".format(dimension))
qb_dimension_fields.append(dimension)
if group_by_account:
dimension_fields.append("t1.account")
qb_dimension_fields.append("account")
return frappe.db.sql(
"""
select
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
where
t1.is_cancelled = 0
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),
),
(self.company, self.get("year_start_date"), self.posting_date),
as_dict=1,
account_filters = {
"company": self.company,
"is_group": 0,
}
if report_type:
account_filters.update({"report_type": report_type})
accounts = frappe.get_all("Account", filters=account_filters, pluck="name")
gl_entry = frappe.qb.DocType("GL Entry")
query = frappe.qb.from_(gl_entry).select(gl_entry.account, gl_entry.account_currency)
if not for_aggregation:
query = query.select(
(Sum(gl_entry.debit_in_account_currency) - Sum(gl_entry.credit_in_account_currency)).as_(
"bal_in_account_currency"
),
(Sum(gl_entry.debit) - Sum(gl_entry.credit)).as_("bal_in_company_currency"),
)
else:
query = query.select(
(Sum(gl_entry.debit_in_account_currency)).as_("debit_in_account_currency"),
(Sum(gl_entry.credit_in_account_currency)).as_("credit_in_account_currency"),
(Sum(gl_entry.debit)).as_("debit"),
(Sum(gl_entry.credit)).as_("credit"),
)
for dimension in qb_dimension_fields:
query = query.select(gl_entry[dimension])
query = query.where(
(gl_entry.company == self.company)
& (gl_entry.is_cancelled == 0)
& (gl_entry.account.isin(accounts))
)
if get_opening_entries:
query = query.where(
gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date)
| gl_entry.is_opening
== "Yes"
)
else:
query = query.where(
gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date)
& gl_entry.is_opening
== "No"
)
def process_gl_entries(gl_entries):
if for_aggregation:
query = query.where(gl_entry.voucher_type != "Period Closing Voucher")
for dimension in qb_dimension_fields:
query = query.groupby(gl_entry[dimension])
return query.run(as_dict=1)
def process_gl_entries(gl_entries, closing_entries, voucher_name, company, closing_date):
from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
make_closing_entries,
)
from erpnext.accounts.general_ledger import make_gl_entries
try:
make_gl_entries(gl_entries, merge_entries=False)
frappe.db.set_value(
"Period Closing Voucher", gl_entries[0].get("voucher_no"), "gle_processing_status", "Completed"
)
if gl_entries:
make_gl_entries(gl_entries, merge_entries=False)
make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date)
frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Completed")
except Exception as e:
frappe.db.rollback()
frappe.log_error(e)
frappe.db.set_value(
"Period Closing Voucher", gl_entries[0].get("voucher_no"), "gle_processing_status", "Failed"
)
frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Failed")
def make_reverse_gl_entries(voucher_type, voucher_no):

View File

@@ -16,16 +16,17 @@ from erpnext.accounts.utils import get_fiscal_year, now
class TestPeriodClosingVoucher(unittest.TestCase):
def test_closing_entry(self):
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'")
company = create_company()
cost_center = create_cost_center("Test Cost Center 1")
jv1 = make_journal_entry(
posting_date="2021-03-15",
amount=400,
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center,
posting_date=now(),
save=False,
)
jv1.company = company
@@ -33,18 +34,18 @@ class TestPeriodClosingVoucher(unittest.TestCase):
jv1.submit()
jv2 = make_journal_entry(
posting_date="2021-03-15",
amount=600,
account1="Cost of Goods Sold - TPC",
account2="Cash - TPC",
cost_center=cost_center,
posting_date=now(),
save=False,
)
jv2.company = company
jv2.save()
jv2.submit()
pcv = self.make_period_closing_voucher()
pcv = self.make_period_closing_voucher(posting_date="2021-03-31")
surplus_account = pcv.closing_account_head
expected_gle = (
@@ -65,6 +66,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
def test_cost_center_wise_posting(self):
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'")
company = create_company()
surplus_account = create_account()
@@ -81,6 +83,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
debit_to="Debtors - TPC",
currency="USD",
customer="_Test Customer USD",
posting_date="2021-03-15",
)
create_sales_invoice(
company=company,
@@ -91,9 +94,10 @@ class TestPeriodClosingVoucher(unittest.TestCase):
debit_to="Debtors - TPC",
currency="USD",
customer="_Test Customer USD",
posting_date="2021-03-15",
)
pcv = self.make_period_closing_voucher(submit=False)
pcv = self.make_period_closing_voucher(posting_date="2021-03-31", submit=False)
pcv.save()
pcv.submit()
surplus_account = pcv.closing_account_head
@@ -128,12 +132,13 @@ class TestPeriodClosingVoucher(unittest.TestCase):
def test_period_closing_with_finance_book_entries(self):
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'")
company = create_company()
surplus_account = create_account()
cost_center = create_cost_center("Test Cost Center 1")
si = create_sales_invoice(
create_sales_invoice(
company=company,
income_account="Sales - TPC",
expense_account="Cost of Goods Sold - TPC",
@@ -142,6 +147,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
debit_to="Debtors - TPC",
currency="USD",
customer="_Test Customer USD",
posting_date="2021-03-15",
)
jv = make_journal_entry(
@@ -149,14 +155,14 @@ class TestPeriodClosingVoucher(unittest.TestCase):
account2="Sales - TPC",
amount=400,
cost_center=cost_center,
posting_date=now(),
posting_date="2021-03-15",
)
jv.company = company
jv.finance_book = create_finance_book().name
jv.save()
jv.submit()
pcv = self.make_period_closing_voucher()
pcv = self.make_period_closing_voucher(posting_date="2021-03-31")
surplus_account = pcv.closing_account_head
expected_gle = (
@@ -194,14 +200,130 @@ class TestPeriodClosingVoucher(unittest.TestCase):
repost_doc.posting_date = add_months(today(), 13)
repost_doc.save()
def make_period_closing_voucher(self, submit=True):
def test_gl_entries_restrictions(self):
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'")
company = create_company()
cost_center = create_cost_center("Test Cost Center 1")
self.make_period_closing_voucher(posting_date="2021-03-31")
jv1 = make_journal_entry(
posting_date="2021-03-15",
amount=400,
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center,
save=False,
)
jv1.company = company
jv1.save()
self.assertRaises(frappe.ValidationError, jv1.submit)
def test_closing_balance_with_dimensions(self):
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'")
frappe.db.sql("delete from `tabAccount Closing Balance` where company='Test PCV Company'")
company = create_company()
cost_center1 = create_cost_center("Test Cost Center 1")
cost_center2 = create_cost_center("Test Cost Center 2")
jv1 = make_journal_entry(
posting_date="2021-03-15",
amount=400,
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center1,
save=False,
)
jv1.company = company
jv1.save()
jv1.submit()
jv2 = make_journal_entry(
posting_date="2021-03-15",
amount=200,
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center2,
save=False,
)
jv2.company = company
jv2.save()
jv2.submit()
pcv1 = self.make_period_closing_voucher(posting_date="2021-03-31")
closing_balance = frappe.db.get_value(
"Account Closing Balance",
{
"account": "Sales - TPC",
"cost_center": cost_center1,
"period_closing_voucher": pcv1.name,
"is_period_closing_voucher_entry": 0,
},
["credit", "credit_in_account_currency"],
as_dict=1,
)
self.assertEqual(closing_balance.credit, 400)
self.assertEqual(closing_balance.credit_in_account_currency, 400)
jv3 = make_journal_entry(
posting_date="2022-03-15",
amount=300,
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center2,
save=False,
)
jv3.company = company
jv3.save()
jv3.submit()
pcv2 = self.make_period_closing_voucher(posting_date="2022-03-31")
cc1_closing_balance = frappe.db.get_value(
"Account Closing Balance",
{
"account": "Sales - TPC",
"cost_center": cost_center1,
"period_closing_voucher": pcv2.name,
"is_period_closing_voucher_entry": 0,
},
["credit", "credit_in_account_currency"],
as_dict=1,
)
cc2_closing_balance = frappe.db.get_value(
"Account Closing Balance",
{
"account": "Sales - TPC",
"cost_center": cost_center2,
"period_closing_voucher": pcv2.name,
"is_period_closing_voucher_entry": 0,
},
["credit", "credit_in_account_currency"],
as_dict=1,
)
self.assertEqual(cc1_closing_balance.credit, 400)
self.assertEqual(cc1_closing_balance.credit_in_account_currency, 400)
self.assertEqual(cc2_closing_balance.credit, 500)
self.assertEqual(cc2_closing_balance.credit_in_account_currency, 500)
def make_period_closing_voucher(self, posting_date=None, submit=True):
surplus_account = create_account()
cost_center = create_cost_center("Test Cost Center 1")
pcv = frappe.get_doc(
{
"doctype": "Period Closing Voucher",
"transaction_date": today(),
"posting_date": today(),
"transaction_date": posting_date or today(),
"posting_date": posting_date or today(),
"company": "Test PCV Company",
"fiscal_year": get_fiscal_year(today(), company="Test PCV Company")[0],
"cost_center": cost_center,

View File

@@ -153,7 +153,7 @@ frappe.ui.form.on('POS Closing Entry', {
frappe.ui.form.on('POS Closing Entry Detail', {
closing_amount: (frm, cdt, cdn) => {
const row = locals[cdt][cdn];
frappe.model.set_value(cdt, cdn, "difference", flt(row.expected_amount - row.closing_amount));
frappe.model.set_value(cdt, cdn, "difference", flt(row.closing_amount - row.expected_amount));
}
})

View File

@@ -130,6 +130,7 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex
args: { "pos_profile": frm.pos_profile },
callback: ({ message: profile }) => {
this.update_customer_groups_settings(profile?.customer_groups);
this.frm.set_value("company", profile?.company);
},
});
}

View File

@@ -54,6 +54,7 @@ class POSInvoice(SalesInvoice):
self.validate_pos()
self.validate_payment_amount()
self.validate_loyalty_transaction()
self.validate_company_with_pos_company()
if self.coupon_code:
from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
@@ -370,6 +371,14 @@ class POSInvoice(SalesInvoice):
if total_amount_in_payments and total_amount_in_payments < invoice_total:
frappe.throw(_("Total payments amount can't be greater than {}").format(-invoice_total))
def validate_company_with_pos_company(self):
if self.company != frappe.db.get_value("POS Profile", self.pos_profile, "company"):
frappe.throw(
_("Company {} does not match with POS Profile Company {}").format(
self.company, frappe.db.get_value("POS Profile", self.pos_profile, "company")
)
)
def validate_loyalty_transaction(self):
if self.redeem_loyalty_points and (
not self.loyalty_redemption_account or not self.loyalty_redemption_cost_center
@@ -448,6 +457,7 @@ class POSInvoice(SalesInvoice):
profile = {}
if self.pos_profile:
profile = frappe.get_doc("POS Profile", self.pos_profile)
self.company = profile.get("company")
if not self.get("payments") and not for_validate:
update_multi_mode_option(self, profile)

View File

@@ -16,8 +16,10 @@ from erpnext.stock.doctype.item.test_item import create_item
class TestProcessDeferredAccounting(unittest.TestCase):
def test_creation_of_ledger_entry_on_submit(self):
"""test creation of gl entries on submission of document"""
change_acc_settings(acc_frozen_upto="2023-05-31", book_deferred_entries_based_on="Months")
deferred_account = create_account(
account_name="Deferred Revenue",
account_name="Deferred Revenue for Accounts Frozen",
parent_account="Current Liabilities - _TC",
company="_Test Company",
)
@@ -29,11 +31,11 @@ class TestProcessDeferredAccounting(unittest.TestCase):
item.save()
si = create_sales_invoice(
item=item.name, update_stock=0, posting_date="2019-01-10", do_not_submit=True
item=item.name, rate=3000, update_stock=0, posting_date="2023-07-01", do_not_submit=True
)
si.items[0].enable_deferred_revenue = 1
si.items[0].service_start_date = "2019-01-10"
si.items[0].service_end_date = "2019-03-15"
si.items[0].service_start_date = "2023-05-01"
si.items[0].service_end_date = "2023-07-31"
si.items[0].deferred_revenue_account = deferred_account
si.save()
si.submit()
@@ -41,9 +43,9 @@ class TestProcessDeferredAccounting(unittest.TestCase):
process_deferred_accounting = doc = frappe.get_doc(
dict(
doctype="Process Deferred Accounting",
posting_date="2019-01-01",
start_date="2019-01-01",
end_date="2019-01-31",
posting_date="2023-07-01",
start_date="2023-05-01",
end_date="2023-06-30",
type="Income",
)
)
@@ -52,11 +54,16 @@ class TestProcessDeferredAccounting(unittest.TestCase):
process_deferred_accounting.submit()
expected_gle = [
[deferred_account, 33.85, 0.0, "2019-01-31"],
["Sales - _TC", 0.0, 33.85, "2019-01-31"],
["Debtors - _TC", 3000, 0.0, "2023-07-01"],
[deferred_account, 0.0, 3000, "2023-07-01"],
["Sales - _TC", 0.0, 1000, "2023-06-30"],
[deferred_account, 1000, 0.0, "2023-06-30"],
["Sales - _TC", 0.0, 1000, "2023-06-30"],
[deferred_account, 1000, 0.0, "2023-06-30"],
]
check_gl_entries(self, si.name, expected_gle, "2019-01-10")
check_gl_entries(self, si.name, expected_gle, "2023-07-01")
change_acc_settings()
def test_pda_submission_and_cancellation(self):
pda = frappe.get_doc(
@@ -70,3 +77,10 @@ class TestProcessDeferredAccounting(unittest.TestCase):
)
pda.submit()
pda.cancel()
def change_acc_settings(acc_frozen_upto="", book_deferred_entries_based_on="Days"):
acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
acc_settings.acc_frozen_upto = acc_frozen_upto
acc_settings.book_deferred_entries_based_on = book_deferred_entries_based_on
acc_settings.save()

View File

@@ -146,7 +146,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-04-21 17:19:30.912953",
"modified": "2023-08-11 10:56:51.699137",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Payment Reconciliation",
@@ -154,15 +154,25 @@
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"role": "Accounts Manager",
"share": 1,
"submit": 1,
"write": 1
},
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"read": 1,
"role": "Accounts User",
"share": 1,
"submit": 1,
"write": 1
}
],

View File

@@ -138,7 +138,7 @@ def get_gl_filters(doc, entry, tax_id, presentation_currency):
def get_ar_filters(doc, entry):
return {
"report_date": doc.posting_date if doc.posting_date else None,
"customer_name": entry.customer,
"customer": entry.customer,
"payment_terms_template": doc.payment_terms_template if doc.payment_terms_template else None,
"sales_partner": doc.sales_partner if doc.sales_partner else None,
"sales_person": doc.sales_person if doc.sales_person else None,

View File

@@ -10,16 +10,12 @@
<h2 class="text-center" style="margin-top:0">{{ _(report.report_name) }}</h2>
<h4 class="text-center">
{% if (filters.customer_name) %}
{{ filters.customer_name }}
{% else %}
{{ filters.customer ~ filters.supplier }}
{% endif %}
{{ filters.customer }}
</h4>
<h6 class="text-center">
{% if (filters.tax_id) %}
{{ _("Tax Id: ") }}{{ filters.tax_id }}
{% endif %}
{% if (filters.tax_id) %}
{{ _("Tax Id: ") }}{{ filters.tax_id }}
{% endif %}
</h6>
<h5 class="text-center">
{{ _(filters.ageing_based_on) }}

View File

@@ -31,7 +31,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
super.onload();
// Ignore linked advances
this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger"];
this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger", "Repost Accounting Ledger"];
if(!this.frm.doc.__islocal) {
// show credit_to in print format

View File

@@ -547,6 +547,7 @@
"depends_on": "update_stock",
"fieldname": "rejected_warehouse",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Rejected Warehouse",
"no_copy": 1,
"options": "Warehouse",
@@ -1573,7 +1574,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
"modified": "2023-04-29 12:57:50.832598",
"modified": "2023-07-04 17:23:59.145031",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -232,7 +232,7 @@ class PurchaseInvoice(BuyingController):
)
if (
cint(frappe.get_cached_value("Buying Settings", "None", "maintain_same_rate"))
cint(frappe.db.get_single_value("Buying Settings", "maintain_same_rate"))
and not self.is_return
and not self.is_internal_supplier
):
@@ -543,6 +543,7 @@ class PurchaseInvoice(BuyingController):
merge_entries=False,
from_repost=from_repost,
)
self.make_exchange_gain_loss_journal()
elif self.docstatus == 2:
provisional_entries = [a for a in gl_entries if a.voucher_type == "Purchase Receipt"]
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
@@ -587,7 +588,6 @@ class PurchaseInvoice(BuyingController):
self.get_asset_gl_entry(gl_entries)
self.make_tax_gl_entries(gl_entries)
self.make_exchange_gain_loss_gl_entries(gl_entries)
self.make_internal_transfer_gl_entries(gl_entries)
gl_entries = make_regional_gl_entries(gl_entries, self)
@@ -976,30 +976,6 @@ class PurchaseInvoice(BuyingController):
item.item_tax_amount, item.precision("item_tax_amount")
)
def make_precision_loss_gl_entry(self, gl_entries):
round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center
)
precision_loss = self.get("base_net_total") - flt(
self.get("net_total") * self.conversion_rate, self.precision("net_total")
)
if precision_loss:
gl_entries.append(
self.get_gl_dict(
{
"account": round_off_account,
"against": self.supplier,
"credit": precision_loss,
"cost_center": round_off_cost_center
if self.use_company_roundoff_cost_center
else self.cost_center or round_off_cost_center,
"remarks": _("Net total calculation precision loss"),
}
)
)
def get_asset_gl_entry(self, gl_entries):
arbnb_account = self.get_company_default("asset_received_but_not_billed")
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
@@ -1446,6 +1422,8 @@ class PurchaseInvoice(BuyingController):
"Repost Item Valuation",
"Repost Payment Ledger",
"Repost Payment Ledger Items",
"Repost Accounting Ledger",
"Repost Accounting Ledger Items",
"Payment Ledger Entry",
"Tax Withheld Vouchers",
)

View File

@@ -1264,10 +1264,11 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
pi.save()
pi.submit()
creditors_account = pi.credit_to
expected_gle = [
["_Test Account Cost for Goods Sold - _TC", 37500.0],
["_Test Payable USD - _TC", -35000.0],
["Exchange Gain/Loss - _TC", -2500.0],
["_Test Payable USD - _TC", -37500.0],
]
gl_entries = frappe.db.sql(
@@ -1284,6 +1285,31 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
self.assertEqual(expected_gle[i][0], gle.account)
self.assertEqual(expected_gle[i][1], gle.balance)
pi.reload()
self.assertEqual(pi.outstanding_amount, 0)
total_debit_amount = frappe.db.get_all(
"Journal Entry Account",
{"account": creditors_account, "docstatus": 1, "reference_name": pi.name},
"sum(debit) as amount",
group_by="reference_name",
)[0].amount
self.assertEqual(flt(total_debit_amount, 2), 2500)
jea_parent = frappe.db.get_all(
"Journal Entry Account",
filters={
"account": creditors_account,
"docstatus": 1,
"reference_name": pi.name,
"debit": 2500,
"debit_in_account_currency": 0,
},
fields=["parent"],
)[0]
self.assertEqual(
frappe.db.get_value("Journal Entry", jea_parent.parent, "voucher_type"), "Exchange Gain Or Loss"
)
pi_2 = make_purchase_invoice(
supplier="_Test Supplier USD",
currency="USD",
@@ -1308,10 +1334,12 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
pi_2.save()
pi_2.submit()
pi_2.reload()
self.assertEqual(pi_2.outstanding_amount, 0)
expected_gle = [
["_Test Account Cost for Goods Sold - _TC", 36500.0],
["_Test Payable USD - _TC", -35000.0],
["Exchange Gain/Loss - _TC", -1500.0],
["_Test Payable USD - _TC", -36500.0],
]
gl_entries = frappe.db.sql(
@@ -1342,12 +1370,39 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
self.assertEqual(expected_gle[i][0], gle.account)
self.assertEqual(expected_gle[i][1], gle.balance)
total_debit_amount = frappe.db.get_all(
"Journal Entry Account",
{"account": creditors_account, "docstatus": 1, "reference_name": pi_2.name},
"sum(debit) as amount",
group_by="reference_name",
)[0].amount
self.assertEqual(flt(total_debit_amount, 2), 1500)
jea_parent_2 = frappe.db.get_all(
"Journal Entry Account",
filters={
"account": creditors_account,
"docstatus": 1,
"reference_name": pi_2.name,
"debit": 1500,
"debit_in_account_currency": 0,
},
fields=["parent"],
)[0]
self.assertEqual(
frappe.db.get_value("Journal Entry", jea_parent_2.parent, "voucher_type"),
"Exchange Gain Or Loss",
)
pi.reload()
pi.cancel()
self.assertEqual(frappe.db.get_value("Journal Entry", jea_parent.parent, "docstatus"), 2)
pi_2.reload()
pi_2.cancel()
self.assertEqual(frappe.db.get_value("Journal Entry", jea_parent_2.parent, "docstatus"), 2)
pay.reload()
pay.cancel()
@@ -1670,23 +1725,147 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
rate = flt(sle.stock_value_difference) / flt(sle.actual_qty)
self.assertAlmostEqual(returned_inv.items[0].rate, rate)
def test_payment_allocation_for_payment_terms(self):
from erpnext.buying.doctype.purchase_order.test_purchase_order import (
create_pr_against_po,
create_purchase_order,
)
from erpnext.selling.doctype.sales_order.test_sales_order import (
automatically_fetch_payment_terms,
)
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
make_purchase_invoice as make_pi_from_pr,
)
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
gl_entries = frappe.db.sql(
"""select account, debit, credit, posting_date
from `tabGL Entry`
where voucher_type='Purchase Invoice' and voucher_no=%s and posting_date >= %s
order by posting_date asc, account asc""",
(voucher_no, posting_date),
as_dict=1,
automatically_fetch_payment_terms()
frappe.db.set_value(
"Payment Terms Template",
"_Test Payment Term Template",
"allocate_payment_based_on_payment_terms",
0,
)
po = create_purchase_order(do_not_save=1)
po.payment_terms_template = "_Test Payment Term Template"
po.save()
po.submit()
pr = create_pr_against_po(po.name, received_qty=4)
pi = make_pi_from_pr(pr.name)
self.assertEqual(pi.payment_schedule[0].payment_amount, 1000)
frappe.db.set_value(
"Payment Terms Template",
"_Test Payment Term Template",
"allocate_payment_based_on_payment_terms",
1,
)
pi = make_pi_from_pr(pr.name)
self.assertEqual(pi.payment_schedule[0].payment_amount, 2500)
automatically_fetch_payment_terms(enable=0)
frappe.db.set_value(
"Payment Terms Template",
"_Test Payment Term Template",
"allocate_payment_based_on_payment_terms",
0,
)
def test_offsetting_entries_for_accounting_dimensions(self):
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.accounts.report.trial_balance.test_trial_balance import (
clear_dimension_defaults,
create_accounting_dimension,
disable_dimension,
)
create_account(
account_name="Offsetting",
company="_Test Company",
parent_account="Temporary Accounts - _TC",
)
create_accounting_dimension(company="_Test Company", offsetting_account="Offsetting - _TC")
branch1 = frappe.new_doc("Branch")
branch1.branch = "Location 1"
branch1.insert(ignore_if_duplicate=True)
branch2 = frappe.new_doc("Branch")
branch2.branch = "Location 2"
branch2.insert(ignore_if_duplicate=True)
pi = make_purchase_invoice(
company="_Test Company",
customer="_Test Supplier",
do_not_save=True,
do_not_submit=True,
rate=1000,
price_list_rate=1000,
qty=1,
)
pi.branch = branch1.branch
pi.items[0].branch = branch2.branch
pi.save()
pi.submit()
expected_gle = [
["_Test Account Cost for Goods Sold - _TC", 1000, 0.0, nowdate(), branch2.branch],
["Creditors - _TC", 0.0, 1000, nowdate(), branch1.branch],
["Offsetting - _TC", 1000, 0.0, nowdate(), branch1.branch],
["Offsetting - _TC", 0.0, 1000, nowdate(), branch2.branch],
]
check_gl_entries(
self,
pi.name,
expected_gle,
nowdate(),
voucher_type="Purchase Invoice",
additional_columns=["branch"],
)
clear_dimension_defaults("Branch")
disable_dimension()
def check_gl_entries(
doc,
voucher_no,
expected_gle,
posting_date,
voucher_type="Purchase Invoice",
additional_columns=None,
):
gl = frappe.qb.DocType("GL Entry")
query = (
frappe.qb.from_(gl)
.select(gl.account, gl.debit, gl.credit, gl.posting_date)
.where(
(gl.voucher_type == voucher_type)
& (gl.voucher_no == voucher_no)
& (gl.posting_date >= posting_date)
& (gl.is_cancelled == 0)
)
.orderby(gl.posting_date, gl.account, gl.creation)
)
if additional_columns:
for col in additional_columns:
query = query.select(gl[col])
gl_entries = query.run(as_dict=True)
for i, gle in enumerate(gl_entries):
doc.assertEqual(expected_gle[i][0], gle.account)
doc.assertEqual(expected_gle[i][1], gle.debit)
doc.assertEqual(expected_gle[i][2], gle.credit)
doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
if additional_columns:
j = 4
for col in additional_columns:
doc.assertEqual(expected_gle[i][j], gle[col])
j += 1
def create_tax_witholding_category(category_name, company, account):
from erpnext.accounts.utils import get_fiscal_year

View File

@@ -176,6 +176,7 @@
"fieldname": "received_qty",
"fieldtype": "Float",
"label": "Received Qty",
"no_copy": 1,
"read_only": 1
},
{
@@ -420,6 +421,7 @@
{
"fieldname": "rejected_warehouse",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Rejected Warehouse",
"options": "Warehouse"
},
@@ -880,7 +882,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2022-11-29 13:01:20.438217",
"modified": "2023-07-04 17:22:21.501152",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
@@ -890,4 +892,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -0,0 +1,44 @@
<style>
.print-format {
padding: 4mm;
font-size: 8.0pt !important;
}
.print-format td {
vertical-align:middle !important;
}
.old {
background-color: #FFB3C0;
}
.new {
background-color: #B3FFCC;
}
</style>
<table class="table table-bordered table-condensed">
<colgroup>
{% for col in gl_columns%}
<col style="width: 18mm;">
{% endfor %}
</colgroup>
<thead>
<tr>
{% for col in gl_columns%}
<td>{{ col.label }}</td>
{% endfor %}
</tr>
</thead>
{% for gl in gl_data%}
{% if gl["old"]%}
<tr class="old">
{% else %}
<tr class="new">
{% endif %}
{% for col in gl_columns %}
<td class="text-right">
{{ gl[col.fieldname] }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>

View File

@@ -0,0 +1,50 @@
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on("Repost Accounting Ledger", {
setup: function(frm) {
frm.fields_dict['vouchers'].grid.get_field('voucher_type').get_query = function(doc) {
return {
filters: {
name: ['in', ['Purchase Invoice', 'Sales Invoice', 'Payment Entry', 'Journal Entry']],
}
}
}
frm.fields_dict['vouchers'].grid.get_field('voucher_no').get_query = function(doc) {
if (doc.company) {
return {
filters: {
company: doc.company,
docstatus: 1
}
}
}
}
},
refresh: function(frm) {
frm.add_custom_button(__('Show Preview'), () => {
frm.call({
method: 'generate_preview',
doc: frm.doc,
freeze: true,
freeze_message: __('Generating Preview'),
callback: function(r) {
if (r && r.message) {
let content = r.message;
let opts = {
title: "Preview",
subtitle: "preview",
content: content,
print_settings: {orientation: "landscape"},
columns: [],
data: [],
}
frappe.render_grid(opts);
}
}
});
});
}
});

View File

@@ -0,0 +1,81 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "format:ACC-REPOST-{#####}",
"creation": "2023-07-04 13:07:32.923675",
"default_view": "List",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"company",
"column_break_vpup",
"delete_cancelled_entries",
"section_break_metl",
"vouchers",
"amended_from"
],
"fields": [
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company"
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Repost Accounting Ledger",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "vouchers",
"fieldtype": "Table",
"label": "Vouchers",
"options": "Repost Accounting Ledger Items"
},
{
"fieldname": "column_break_vpup",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_metl",
"fieldtype": "Section Break"
},
{
"default": "0",
"fieldname": "delete_cancelled_entries",
"fieldtype": "Check",
"label": "Delete Cancelled Ledger Entries"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-07-27 15:47:58.975034",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Repost Accounting Ledger",
"naming_rule": "Expression",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}

View File

@@ -0,0 +1,183 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _, qb
from frappe.model.document import Document
from frappe.utils.data import comma_and
class RepostAccountingLedger(Document):
def __init__(self, *args, **kwargs):
super(RepostAccountingLedger, self).__init__(*args, **kwargs)
self._allowed_types = set(
["Purchase Invoice", "Sales Invoice", "Payment Entry", "Journal Entry"]
)
def validate(self):
self.validate_vouchers()
self.validate_for_closed_fiscal_year()
self.validate_for_deferred_accounting()
def validate_for_deferred_accounting(self):
sales_docs = [x.voucher_no for x in self.vouchers if x.voucher_type == "Sales Invoice"]
docs_with_deferred_revenue = frappe.db.get_all(
"Sales Invoice Item",
filters={"parent": ["in", sales_docs], "docstatus": 1, "enable_deferred_revenue": True},
fields=["parent"],
as_list=1,
)
purchase_docs = [x.voucher_no for x in self.vouchers if x.voucher_type == "Purchase Invoice"]
docs_with_deferred_expense = frappe.db.get_all(
"Purchase Invoice Item",
filters={"parent": ["in", purchase_docs], "docstatus": 1, "enable_deferred_expense": 1},
fields=["parent"],
as_list=1,
)
if docs_with_deferred_revenue or docs_with_deferred_expense:
frappe.throw(
_("Documents: {0} have deferred revenue/expense enabled for them. Cannot repost.").format(
frappe.bold(
comma_and([x[0] for x in docs_with_deferred_expense + docs_with_deferred_revenue])
)
)
)
def validate_for_closed_fiscal_year(self):
if self.vouchers:
latest_pcv = (
frappe.db.get_all(
"Period Closing Voucher",
filters={"company": self.company},
order_by="posting_date desc",
pluck="posting_date",
limit=1,
)
or None
)
if not latest_pcv:
return
for vtype in self._allowed_types:
if names := [x.voucher_no for x in self.vouchers if x.voucher_type == vtype]:
latest_voucher = frappe.db.get_all(
vtype,
filters={"name": ["in", names]},
pluck="posting_date",
order_by="posting_date desc",
limit=1,
)[0]
if latest_voucher and latest_pcv[0] >= latest_voucher:
frappe.throw(_("Cannot Resubmit Ledger entries for vouchers in Closed fiscal year."))
def validate_vouchers(self):
if self.vouchers:
# Validate voucher types
voucher_types = set([x.voucher_type for x in self.vouchers])
if disallowed_types := voucher_types.difference(self._allowed_types):
frappe.throw(
_("{0} types are not allowed. Only {1} are.").format(
frappe.bold(comma_and(list(disallowed_types))),
frappe.bold(comma_and(list(self._allowed_types))),
)
)
def get_existing_ledger_entries(self):
vouchers = [x.voucher_no for x in self.vouchers]
gl = qb.DocType("GL Entry")
existing_gles = (
qb.from_(gl)
.select(gl.star)
.where((gl.voucher_no.isin(vouchers)) & (gl.is_cancelled == 0))
.run(as_dict=True)
)
self.gles = frappe._dict({})
for gle in existing_gles:
self.gles.setdefault((gle.voucher_type, gle.voucher_no), frappe._dict({})).setdefault(
"existing", []
).append(gle.update({"old": True}))
def generate_preview_data(self):
self.gl_entries = []
self.get_existing_ledger_entries()
for x in self.vouchers:
doc = frappe.get_doc(x.voucher_type, x.voucher_no)
if doc.doctype in ["Payment Entry", "Journal Entry"]:
gle_map = doc.build_gl_map()
else:
gle_map = doc.get_gl_entries()
old_entries = self.gles.get((x.voucher_type, x.voucher_no))
if old_entries:
self.gl_entries.extend(old_entries.existing)
self.gl_entries.extend(gle_map)
@frappe.whitelist()
def generate_preview(self):
from erpnext.accounts.report.general_ledger.general_ledger import get_columns as get_gl_columns
gl_columns = []
gl_data = []
self.generate_preview_data()
if self.gl_entries:
filters = {"company": self.company, "include_dimensions": 1}
for x in get_gl_columns(filters):
if x["fieldname"] == "gl_entry":
x["fieldname"] = "name"
gl_columns.append(x)
gl_data = self.gl_entries
rendered_page = frappe.render_template(
"erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.html",
{"gl_columns": gl_columns, "gl_data": gl_data},
)
return rendered_page
def on_submit(self):
job_name = "repost_accounting_ledger_" + self.name
frappe.enqueue(
method="erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger.start_repost",
account_repost_doc=self.name,
is_async=True,
job_name=job_name,
)
frappe.msgprint(_("Repost has started in the background"))
@frappe.whitelist()
def start_repost(account_repost_doc=str) -> None:
if account_repost_doc:
repost_doc = frappe.get_doc("Repost Accounting Ledger", account_repost_doc)
if repost_doc.docstatus == 1:
# Prevent repost on invoices with deferred accounting
repost_doc.validate_for_deferred_accounting()
for x in repost_doc.vouchers:
doc = frappe.get_doc(x.voucher_type, x.voucher_no)
if repost_doc.delete_cancelled_entries:
frappe.db.delete("GL Entry", filters={"voucher_type": doc.doctype, "voucher_no": doc.name})
frappe.db.delete(
"Payment Ledger Entry", filters={"voucher_type": doc.doctype, "voucher_no": doc.name}
)
if doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
if not repost_doc.delete_cancelled_entries:
doc.docstatus = 2
doc.make_gl_entries_on_cancel()
doc.docstatus = 1
doc.make_gl_entries()
elif doc.doctype in ["Payment Entry", "Journal Entry"]:
if not repost_doc.delete_cancelled_entries:
doc.make_gl_entries(1)
doc.make_gl_entries()
frappe.db.commit()

View File

@@ -0,0 +1,202 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import frappe
from frappe import qb
from frappe.query_builder.functions import Sum
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, nowdate, today
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import start_repost
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.accounts.utils import get_fiscal_year
class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase):
def setUp(self):
self.create_company()
self.create_customer()
self.create_item()
def teadDown(self):
frappe.db.rollback()
def test_01_basic_functions(self):
si = create_sales_invoice(
item=self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
rate=100,
)
preq = frappe.get_doc(
make_payment_request(
dt=si.doctype,
dn=si.name,
payment_request_type="Inward",
party_type="Customer",
party=si.customer,
)
)
preq.save().submit()
# Test Validation Error
ral = frappe.new_doc("Repost Accounting Ledger")
ral.company = self.company
ral.delete_cancelled_entries = True
ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
ral.append(
"vouchers", {"voucher_type": preq.doctype, "voucher_no": preq.name}
) # this should throw validation error
self.assertRaises(frappe.ValidationError, ral.save)
ral.vouchers.pop()
preq.cancel()
preq.delete()
pe = get_payment_entry(si.doctype, si.name)
pe.save().submit()
ral.append("vouchers", {"voucher_type": pe.doctype, "voucher_no": pe.name})
ral.save()
# manually set an incorrect debit amount in DB
gle = frappe.db.get_all("GL Entry", filters={"voucher_no": si.name, "account": self.debit_to})
frappe.db.set_value("GL Entry", gle[0], "debit", 90)
gl = qb.DocType("GL Entry")
res = (
qb.from_(gl)
.select(gl.voucher_no, Sum(gl.debit).as_("debit"), Sum(gl.credit).as_("credit"))
.where((gl.voucher_no == si.name) & (gl.is_cancelled == 0))
.run()
)
# Assert incorrect ledger balance
self.assertNotEqual(res[0], (si.name, 100, 100))
# Submit repost document
ral.save().submit()
# background jobs don't run on test cases. Manually triggering repost function.
start_repost(ral.name)
res = (
qb.from_(gl)
.select(gl.voucher_no, Sum(gl.debit).as_("debit"), Sum(gl.credit).as_("credit"))
.where((gl.voucher_no == si.name) & (gl.is_cancelled == 0))
.run()
)
# Ledger should reflect correct amount post repost
self.assertEqual(res[0], (si.name, 100, 100))
def test_02_deferred_accounting_valiations(self):
si = create_sales_invoice(
item=self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
rate=100,
do_not_submit=True,
)
si.items[0].enable_deferred_revenue = True
si.items[0].deferred_revenue_account = self.deferred_revenue
si.items[0].service_start_date = nowdate()
si.items[0].service_end_date = add_days(nowdate(), 90)
si.save().submit()
ral = frappe.new_doc("Repost Accounting Ledger")
ral.company = self.company
ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
self.assertRaises(frappe.ValidationError, ral.save)
@change_settings("Accounts Settings", {"delete_linked_ledger_entries": 1})
def test_04_pcv_validation(self):
# Clear old GL entries so PCV can be submitted.
gl = frappe.qb.DocType("GL Entry")
qb.from_(gl).delete().where(gl.company == self.company).run()
si = create_sales_invoice(
item=self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
rate=100,
)
pcv = frappe.get_doc(
{
"doctype": "Period Closing Voucher",
"transaction_date": today(),
"posting_date": today(),
"company": self.company,
"fiscal_year": get_fiscal_year(today(), company=self.company)[0],
"cost_center": self.cost_center,
"closing_account_head": self.retained_earnings,
"remarks": "test",
}
)
pcv.save().submit()
ral = frappe.new_doc("Repost Accounting Ledger")
ral.company = self.company
ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
self.assertRaises(frappe.ValidationError, ral.save)
pcv.reload()
pcv.cancel()
pcv.delete()
def test_03_deletion_flag_and_preview_function(self):
si = create_sales_invoice(
item=self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
rate=100,
)
pe = get_payment_entry(si.doctype, si.name)
pe.save().submit()
# without deletion flag set
ral = frappe.new_doc("Repost Accounting Ledger")
ral.company = self.company
ral.delete_cancelled_entries = False
ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
ral.append("vouchers", {"voucher_type": pe.doctype, "voucher_no": pe.name})
ral.save()
# assert preview data is generated
preview = ral.generate_preview()
self.assertIsNotNone(preview)
ral.save().submit()
# background jobs don't run on test cases. Manually triggering repost function.
start_repost(ral.name)
self.assertIsNotNone(frappe.db.exists("GL Entry", {"voucher_no": si.name, "is_cancelled": 1}))
self.assertIsNotNone(frappe.db.exists("GL Entry", {"voucher_no": pe.name, "is_cancelled": 1}))
# with deletion flag set
ral = frappe.new_doc("Repost Accounting Ledger")
ral.company = self.company
ral.delete_cancelled_entries = True
ral.append("vouchers", {"voucher_type": si.doctype, "voucher_no": si.name})
ral.append("vouchers", {"voucher_type": pe.doctype, "voucher_no": pe.name})
ral.save().submit()
start_repost(ral.name)
self.assertIsNone(frappe.db.exists("GL Entry", {"voucher_no": si.name, "is_cancelled": 1}))
self.assertIsNone(frappe.db.exists("GL Entry", {"voucher_no": pe.name, "is_cancelled": 1}))

View File

@@ -0,0 +1,40 @@
{
"actions": [],
"allow_rename": 1,
"creation": "2023-07-04 14:14:01.243848",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"voucher_type",
"voucher_no"
],
"fields": [
{
"fieldname": "voucher_type",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Voucher Type",
"options": "DocType"
},
{
"fieldname": "voucher_no",
"fieldtype": "Dynamic Link",
"in_list_view": 1,
"label": "Voucher No",
"options": "voucher_type"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2023-07-04 14:15:51.165584",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Repost Accounting Ledger Items",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}

View File

@@ -0,0 +1,9 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
# import frappe
from frappe.model.document import Document
class RepostAccountingLedgerItems(Document):
pass

View File

@@ -34,7 +34,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
super.onload();
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log',
'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger"];
'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger", "Repost Accounting Ledger"];
if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
// show debit_to in print format
@@ -670,19 +670,6 @@ frappe.ui.form.on('Sales Invoice', {
}
}
// expense account
frm.fields_dict['items'].grid.get_field('expense_account').get_query = function(doc) {
if (erpnext.is_perpetual_inventory_enabled(doc.company)) {
return {
filters: {
'report_type': 'Profit and Loss',
'company': doc.company,
"is_group": 0
}
}
}
}
// discount account
frm.fields_dict['items'].grid.get_field('discount_account').get_query = function(doc) {
return {
@@ -900,6 +887,8 @@ frappe.ui.form.on('Sales Invoice', {
frm.events.append_time_log(frm, timesheet, 1.0);
}
});
frm.refresh_field("timesheets");
frm.trigger("calculate_timesheet_totals");
},
async get_exchange_rate(frm, from_currency, to_currency) {
@@ -939,9 +928,6 @@ frappe.ui.form.on('Sales Invoice', {
row.billing_amount = flt(time_log.billing_amount) * flt(exchange_rate);
row.timesheet_detail = time_log.name;
row.project_name = time_log.project_name;
frm.refresh_field("timesheets");
frm.trigger("calculate_timesheet_totals");
},
calculate_timesheet_totals: function(frm) {

View File

@@ -714,6 +714,7 @@
"fieldtype": "Table",
"hide_days": 1,
"hide_seconds": 1,
"label": "Items",
"oldfieldname": "entries",
"oldfieldtype": "Table",
"options": "Sales Invoice Item",

View File

@@ -23,7 +23,7 @@ from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category
)
from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center
from erpnext.accounts.party import get_due_date, get_party_account, get_party_details
from erpnext.accounts.utils import get_account_currency
from erpnext.accounts.utils import cancel_exchange_gain_loss_journal, get_account_currency
from erpnext.assets.doctype.asset.depreciation import (
depreciate_asset,
get_disposal_account_and_cost_center,
@@ -399,6 +399,8 @@ class SalesInvoice(SellingController):
"Repost Item Valuation",
"Repost Payment Ledger",
"Repost Payment Ledger Items",
"Repost Accounting Ledger",
"Repost Accounting Ledger Items",
"Payment Ledger Entry",
)
@@ -1046,7 +1048,10 @@ class SalesInvoice(SellingController):
merge_entries=False,
from_repost=from_repost,
)
self.make_exchange_gain_loss_journal()
elif self.docstatus == 2:
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
if update_outstanding == "No":
@@ -1071,10 +1076,10 @@ class SalesInvoice(SellingController):
self.make_customer_gl_entry(gl_entries)
self.make_tax_gl_entries(gl_entries)
self.make_exchange_gain_loss_gl_entries(gl_entries)
self.make_internal_transfer_gl_entries(gl_entries)
self.make_item_gl_entries(gl_entries)
self.make_precision_loss_gl_entry(gl_entries)
self.make_discount_gl_entries(gl_entries)
# merge gl entries before adding pos entries
@@ -1664,15 +1669,13 @@ class SalesInvoice(SellingController):
frappe.db.set_value("Customer", self.customer, "loyalty_program_tier", lp_details.tier_name)
def get_returned_amount(self):
from frappe.query_builder.functions import Coalesce, Sum
from frappe.query_builder.functions import Sum
doc = frappe.qb.DocType(self.doctype)
returned_amount = (
frappe.qb.from_(doc)
.select(Sum(doc.grand_total))
.where(
(doc.docstatus == 1) & (doc.is_return == 1) & (Coalesce(doc.return_against, "") == self.name)
)
.where((doc.docstatus == 1) & (doc.is_return == 1) & (doc.return_against == self.name))
).run()
return abs(returned_amount[0][0]) if returned_amount[0][0] else 0

View File

@@ -15,6 +15,7 @@ def get_data():
},
"internal_links": {
"Sales Order": ["items", "sales_order"],
"Delivery Note": ["items", "delivery_note"],
"Timesheet": ["timesheets", "time_sheet"],
},
"transactions": [

View File

@@ -1900,16 +1900,22 @@ class TestSalesInvoice(unittest.TestCase):
si = self.create_si_to_test_tax_breakup()
itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(si)
itemised_tax_data = get_itemised_tax_breakup_data(si)
expected_itemised_tax = {
"_Test Item": {"Service Tax": {"tax_rate": 10.0, "tax_amount": 1000.0}},
"_Test Item 2": {"Service Tax": {"tax_rate": 10.0, "tax_amount": 500.0}},
}
expected_itemised_taxable_amount = {"_Test Item": 10000.0, "_Test Item 2": 5000.0}
expected_itemised_tax = [
{
"item": "_Test Item",
"taxable_amount": 10000.0,
"Service Tax": {"tax_rate": 10.0, "tax_amount": 1000.0},
},
{
"item": "_Test Item 2",
"taxable_amount": 5000.0,
"Service Tax": {"tax_rate": 10.0, "tax_amount": 500.0},
},
]
self.assertEqual(itemised_tax, expected_itemised_tax)
self.assertEqual(itemised_taxable_amount, expected_itemised_taxable_amount)
self.assertEqual(itemised_tax_data, expected_itemised_tax)
frappe.flags.country = None
@@ -2043,28 +2049,27 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(si.total_taxes_and_charges, 228.82)
self.assertEqual(si.rounding_adjustment, -0.01)
expected_values = dict(
(d[0], d)
for d in [
[si.debit_to, 1500, 0.0],
["_Test Account Service Tax - _TC", 0.0, 114.41],
["_Test Account VAT - _TC", 0.0, 114.41],
["Sales - _TC", 0.0, 1271.18],
]
)
expected_values = [
["_Test Account Service Tax - _TC", 0.0, 114.41],
["_Test Account VAT - _TC", 0.0, 114.41],
[si.debit_to, 1500, 0.0],
["Round Off - _TC", 0.01, 0.01],
["Sales - _TC", 0.0, 1271.18],
]
gl_entries = frappe.db.sql(
"""select account, debit, credit
"""select account, sum(debit) as debit, sum(credit) as credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
group by account
order by account asc""",
si.name,
as_dict=1,
)
for gle in gl_entries:
self.assertEqual(expected_values[gle.account][0], gle.account)
self.assertEqual(expected_values[gle.account][1], gle.debit)
self.assertEqual(expected_values[gle.account][2], gle.credit)
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[i][0], gle.account)
self.assertEqual(expected_values[i][1], gle.debit)
self.assertEqual(expected_values[i][2], gle.credit)
def test_rounding_adjustment_3(self):
from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import (
@@ -2119,13 +2124,14 @@ class TestSalesInvoice(unittest.TestCase):
["_Test Account Service Tax - _TC", 0.0, 240.43],
["_Test Account VAT - _TC", 0.0, 240.43],
["Sales - _TC", 0.0, 4007.15],
["Round Off - _TC", 0.01, 0],
["Round Off - _TC", 0.02, 0.01],
]
)
gl_entries = frappe.db.sql(
"""select account, debit, credit
"""select account, sum(debit) as debit, sum(credit) as credit
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
group by account
order by account asc""",
si.name,
as_dict=1,
@@ -3207,17 +3213,10 @@ class TestSalesInvoice(unittest.TestCase):
account.disabled = 0
account.save()
@change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
def test_gain_loss_with_advance_entry(self):
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
unlink_enabled = frappe.db.get_value(
"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice"
)
frappe.db.set_value(
"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", 1
)
jv = make_journal_entry("_Test Receivable USD - _TC", "_Test Bank - _TC", -7000, save=False)
jv.accounts[0].exchange_rate = 70
@@ -3250,17 +3249,28 @@ class TestSalesInvoice(unittest.TestCase):
)
si.save()
si.submit()
expected_gle = [
["_Test Receivable USD - _TC", 7500.0, 500],
["Exchange Gain/Loss - _TC", 500.0, 0.0],
["Sales - _TC", 0.0, 7500.0],
["_Test Receivable USD - _TC", 7500.0, 0.0, nowdate()],
["Sales - _TC", 0.0, 7500.0, nowdate()],
]
check_gl_entries(self, si.name, expected_gle, nowdate())
frappe.db.set_value(
"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", unlink_enabled
si.reload()
self.assertEqual(si.outstanding_amount, 0)
journals = frappe.db.get_all(
"Journal Entry Account",
filters={"reference_type": "Sales Invoice", "reference_name": si.name, "docstatus": 1},
pluck="parent",
)
journals = [x for x in journals if x != jv.name]
self.assertEqual(len(journals), 1)
je_type = frappe.get_cached_value("Journal Entry", journals[0], "voucher_type")
self.assertEqual(je_type, "Exchange Gain Or Loss")
ledger_outstanding = frappe.db.get_all(
"Payment Ledger Entry",
filters={"against_voucher_no": si.name, "delinked": 0},
fields=["sum(amount), sum(amount_in_account_currency)"],
as_list=1,
)
def test_batch_expiry_for_sales_invoice_return(self):
@@ -3310,6 +3320,14 @@ class TestSalesInvoice(unittest.TestCase):
)
self.assertRaises(frappe.ValidationError, si.submit)
@change_settings("Selling Settings", {"allow_negative_rates_for_items": 0})
def test_sales_return_negative_rate(self):
si = create_sales_invoice(is_return=1, qty=-2, rate=-10, do_not_save=True)
self.assertRaises(frappe.ValidationError, si.save)
si.items[0].rate = 10
si.save()
def get_sales_invoice_for_e_invoice():
si = make_sales_invoice_for_ewaybill()

View File

@@ -603,7 +603,8 @@
"in_list_view": 1,
"label": "Batch No",
"options": "Batch",
"print_hide": 1
"print_hide": 1,
"search_index": 1
},
{
"fieldname": "col_break5",
@@ -890,7 +891,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2022-10-17 12:51:44.825398",
"modified": "2023-07-25 11:58:10.723833",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",

View File

@@ -3,8 +3,10 @@
import frappe
from frappe import _
from frappe import _, qb
from frappe.model.document import Document
from frappe.query_builder import Criterion
from frappe.query_builder.functions import Abs, Sum
from frappe.utils import cint, flt, getdate
@@ -260,14 +262,20 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
if tax_deducted:
net_total = inv.tax_withholding_net_total
if ldc:
tax_amount = get_tds_amount_from_ldc(ldc, parties, tax_details, posting_date, net_total)
limit_consumed = get_limit_consumed(ldc, parties)
if is_valid_certificate(ldc, posting_date, limit_consumed):
tax_amount = get_lower_deduction_amount(
net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details
)
else:
tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0
else:
tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0
# once tds is deducted, not need to add vouchers in the invoice
voucher_wise_amount = {}
else:
tax_amount = get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers)
tax_amount = get_tds_amount(ldc, parties, inv, tax_details, vouchers)
elif party_type == "Customer":
if tax_deducted:
@@ -346,26 +354,33 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
def get_advance_vouchers(
parties, company=None, from_date=None, to_date=None, party_type="Supplier"
):
# for advance vouchers, debit and credit is reversed
dr_or_cr = "debit" if party_type == "Supplier" else "credit"
"""
Use Payment Ledger to fetch unallocated Advance Payments
"""
filters = {
dr_or_cr: [">", 0],
"is_opening": "No",
"is_cancelled": 0,
"party_type": party_type,
"party": ["in", parties],
}
ple = qb.DocType("Payment Ledger Entry")
if party_type == "Customer":
filters.update({"against_voucher": ["is", "not set"]})
conditions = []
conditions.append(ple.amount.lt(0))
conditions.append(ple.delinked == 0)
conditions.append(ple.party_type == party_type)
conditions.append(ple.party.isin(parties))
conditions.append(ple.voucher_no == ple.against_voucher_no)
if company:
filters["company"] = company
if from_date and to_date:
filters["posting_date"] = ["between", (from_date, to_date)]
conditions.append(ple.company == company)
return frappe.get_all("GL Entry", filters=filters, distinct=1, pluck="voucher_no") or [""]
if from_date and to_date:
conditions.append(ple.posting_date[from_date:to_date])
advances = (
qb.from_(ple).select(ple.voucher_no).distinct().where(Criterion.all(conditions)).run(as_list=1)
)
if advances:
advances = [x[0] for x in advances]
return advances
def get_taxes_deducted_on_advances_allocated(inv, tax_details):
@@ -407,7 +422,7 @@ def get_deducted_tax(taxable_vouchers, tax_details):
return sum(entries)
def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
tds_amount = 0
invoice_filters = {"name": ("in", vouchers), "docstatus": 1, "apply_tds": 1}
@@ -467,7 +482,12 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
threshold = tax_details.get("threshold", 0)
cumulative_threshold = tax_details.get("cumulative_threshold", 0)
if (threshold and inv.tax_withholding_net_total >= threshold) or (
if inv.doctype != "Payment Entry":
tax_withholding_net_total = inv.base_tax_withholding_net_total
else:
tax_withholding_net_total = inv.tax_withholding_net_total
if (threshold and tax_withholding_net_total >= threshold) or (
cumulative_threshold and supp_credit_amt >= cumulative_threshold
):
if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(
@@ -482,15 +502,10 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
net_total += inv.tax_withholding_net_total
supp_credit_amt = net_total - cumulative_threshold
if ldc and is_valid_certificate(
ldc.valid_from,
ldc.valid_upto,
inv.get("posting_date") or inv.get("transaction_date"),
tax_deducted,
inv.tax_withholding_net_total,
ldc.certificate_limit,
):
tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details)
if ldc and is_valid_certificate(ldc, inv.get("posting_date") or inv.get("transaction_date"), 0):
tds_amount = get_lower_deduction_amount(
supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details
)
else:
tds_amount = supp_credit_amt * tax_details.rate / 100 if supp_credit_amt > 0 else 0
@@ -499,6 +514,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
tcs_amount = 0
ple = qb.DocType("Payment Ledger Entry")
# sum of debit entries made from sales invoices
invoiced_amt = (
@@ -516,18 +532,20 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
)
# sum of credit entries made from PE / JV with unset 'against voucher'
conditions = []
conditions.append(ple.amount.lt(0))
conditions.append(ple.delinked == 0)
conditions.append(ple.party.isin(parties))
conditions.append(ple.voucher_no == ple.against_voucher_no)
conditions.append(ple.company == inv.company)
advances = (
qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run(as_list=1)
)
advance_amt = (
frappe.db.get_value(
"GL Entry",
{
"is_cancelled": 0,
"party": ["in", parties],
"company": inv.company,
"voucher_no": ["in", adv_vouchers],
},
"sum(credit)",
)
or 0.0
qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run()[0][0] or 0.0
)
# sum of credit entries made from sales invoice
@@ -565,8 +583,7 @@ def get_invoice_total_without_tcs(inv, tax_details):
return inv.grand_total - tcs_tax_row_amount
def get_tds_amount_from_ldc(ldc, parties, tax_details, posting_date, net_total):
tds_amount = 0
def get_limit_consumed(ldc, parties):
limit_consumed = frappe.db.get_value(
"Purchase Invoice",
{
@@ -580,37 +597,29 @@ def get_tds_amount_from_ldc(ldc, parties, tax_details, posting_date, net_total):
"sum(tax_withholding_net_total)",
)
if is_valid_certificate(
ldc.valid_from, ldc.valid_upto, posting_date, limit_consumed, net_total, ldc.certificate_limit
):
tds_amount = get_ltds_amount(
net_total, limit_consumed, ldc.certificate_limit, ldc.rate, tax_details
)
return tds_amount
return limit_consumed
def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details):
if certificate_limit - flt(deducted_amount) - flt(current_amount) >= 0:
def get_lower_deduction_amount(
current_amount, limit_consumed, certificate_limit, rate, tax_details
):
if certificate_limit - flt(limit_consumed) - flt(current_amount) >= 0:
return current_amount * rate / 100
else:
ltds_amount = certificate_limit - flt(deducted_amount)
ltds_amount = certificate_limit - flt(limit_consumed)
tds_amount = current_amount - ltds_amount
return ltds_amount * rate / 100 + tds_amount * tax_details.rate / 100
def is_valid_certificate(
valid_from, valid_upto, posting_date, deducted_amount, current_amount, certificate_limit
):
valid = False
def is_valid_certificate(ldc, posting_date, limit_consumed):
available_amount = flt(ldc.certificate_limit) - flt(limit_consumed)
if (
getdate(ldc.valid_from) <= getdate(posting_date) <= getdate(ldc.valid_upto)
) and available_amount > 0:
return True
available_amount = flt(certificate_limit) - flt(deducted_amount)
if (getdate(valid_from) <= getdate(posting_date) <= getdate(valid_upto)) and available_amount > 0:
valid = True
return valid
return False
def normal_round(number):

View File

@@ -4,6 +4,8 @@
import unittest
import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.tests.utils import change_settings
from frappe.utils import today
from erpnext.accounts.utils import get_fiscal_year
@@ -17,6 +19,7 @@ class TestTaxWithholdingCategory(unittest.TestCase):
# create relevant supplier, etc
create_records()
create_tax_withholding_category_records()
make_pan_no_field()
def tearDown(self):
cancel_invoices()
@@ -152,6 +155,64 @@ class TestTaxWithholdingCategory(unittest.TestCase):
for d in reversed(invoices):
d.cancel()
@change_settings(
"Accounts Settings",
{"unlink_payment_on_cancellation_of_invoice": 1},
)
def test_tcs_on_unallocated_advance_payments(self):
frappe.db.set_value(
"Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS"
)
vouchers = []
# create advance payment
pe = create_payment_entry(
payment_type="Receive", party_type="Customer", party="Test TCS Customer", paid_amount=20000
)
pe.paid_from = "Debtors - _TC"
pe.paid_to = "Cash - _TC"
pe.submit()
vouchers.append(pe)
# create invoice
si1 = create_sales_invoice(customer="Test TCS Customer", rate=5000)
si1.submit()
vouchers.append(si1)
# reconcile
pr = frappe.get_doc("Payment Reconciliation")
pr.company = "_Test Company"
pr.party_type = "Customer"
pr.party = "Test TCS Customer"
pr.receivable_payable_account = "Debtors - _TC"
pr.get_unreconciled_entries()
invoices = [x.as_dict() for x in pr.get("invoices")]
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.reconcile()
# make another invoice
# sum of unallocated amount from payment entry and this sales invoice will breach cumulative threashold
# TDS should be calculated
si2 = create_sales_invoice(customer="Test TCS Customer", rate=15000)
si2.submit()
vouchers.append(si2)
si3 = create_sales_invoice(customer="Test TCS Customer", rate=10000)
si3.submit()
vouchers.append(si3)
# assert tax collection on total invoice amount created until now
tcs_charged = sum([d.base_tax_amount for d in si2.taxes if d.account_head == "TCS - _TC"])
tcs_charged += sum([d.base_tax_amount for d in si3.taxes if d.account_head == "TCS - _TC"])
self.assertEqual(tcs_charged, 1500)
# cancel invoice and payments to avoid clashing
for d in reversed(vouchers):
d.reload()
d.cancel()
def test_tds_calculation_on_net_total(self):
frappe.db.set_value(
"Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS"
@@ -262,6 +323,42 @@ class TestTaxWithholdingCategory(unittest.TestCase):
for d in reversed(orders):
d.cancel()
def test_tds_deduction_for_po_via_payment_entry(self):
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
frappe.db.set_value(
"Supplier", "Test TDS Supplier8", "tax_withholding_category", "Cumulative Threshold TDS"
)
order = create_purchase_order(supplier="Test TDS Supplier8", rate=40000, do_not_save=True)
# Add some tax on the order
order.append(
"taxes",
{
"category": "Total",
"charge_type": "Actual",
"account_head": "_Test Account VAT - _TC",
"cost_center": "Main - _TC",
"tax_amount": 8000,
"description": "Test",
"add_deduct_tax": "Add",
},
)
order.save()
order.apply_tds = 1
order.tax_withholding_category = "Cumulative Threshold TDS"
order.submit()
self.assertEqual(order.taxes[0].tax_amount, 4000)
payment = get_payment_entry(order.doctype, order.name)
payment.apply_tax_withholding_amount = 1
payment.tax_withholding_category = "Cumulative Threshold TDS"
payment.submit()
self.assertEqual(payment.taxes[0].tax_amount, 4000)
def test_multi_category_single_supplier(self):
frappe.db.set_value(
"Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category"
@@ -361,6 +458,40 @@ class TestTaxWithholdingCategory(unittest.TestCase):
pe2.cancel()
pe3.cancel()
def test_lower_deduction_certificate_application(self):
frappe.db.set_value(
"Supplier",
"Test LDC Supplier",
{
"tax_withholding_category": "Test Service Category",
"pan": "ABCTY1234D",
},
)
create_lower_deduction_certificate(
supplier="Test LDC Supplier",
certificate_no="1AE0423AAJ",
tax_withholding_category="Test Service Category",
tax_rate=2,
limit=50000,
)
pi1 = create_purchase_invoice(supplier="Test LDC Supplier", rate=35000)
pi1.submit()
self.assertEqual(pi1.taxes[0].tax_amount, 700)
pi2 = create_purchase_invoice(supplier="Test LDC Supplier", rate=35000)
pi2.submit()
self.assertEqual(pi2.taxes[0].tax_amount, 2300)
pi3 = create_purchase_invoice(supplier="Test LDC Supplier", rate=35000)
pi3.submit()
self.assertEqual(pi3.taxes[0].tax_amount, 3500)
pi1.cancel()
pi2.cancel()
pi3.cancel()
def cancel_invoices():
purchase_invoices = frappe.get_all(
@@ -519,6 +650,8 @@ def create_records():
"Test TDS Supplier5",
"Test TDS Supplier6",
"Test TDS Supplier7",
"Test TDS Supplier8",
"Test LDC Supplier",
]:
if frappe.db.exists("Supplier", name):
continue
@@ -715,3 +848,39 @@ def create_tax_withholding_category(
"accounts": [{"company": "_Test Company", "account": account}],
}
).insert()
def create_lower_deduction_certificate(
supplier, tax_withholding_category, tax_rate, certificate_no, limit
):
fiscal_year = get_fiscal_year(today(), company="_Test Company")
if not frappe.db.exists("Lower Deduction Certificate", certificate_no):
frappe.get_doc(
{
"doctype": "Lower Deduction Certificate",
"company": "_Test Company",
"supplier": supplier,
"certificate_no": certificate_no,
"tax_withholding_category": tax_withholding_category,
"fiscal_year": fiscal_year[0],
"valid_from": fiscal_year[1],
"valid_upto": fiscal_year[2],
"rate": tax_rate,
"certificate_limit": limit,
}
).insert()
def make_pan_no_field():
pan_field = {
"Supplier": [
{
"fieldname": "pan",
"label": "PAN",
"fieldtype": "Data",
"translatable": 0,
}
]
}
create_custom_fields(pan_field, update=1)

View File

@@ -13,14 +13,11 @@ import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
from erpnext.accounts.doctype.accounting_period.accounting_period import ClosedAccountingPeriod
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
from erpnext.accounts.utils import create_payment_ledger_entry
class ClosedAccountingPeriod(frappe.ValidationError):
pass
def make_gl_entries(
gl_map,
cancel=False,
@@ -31,6 +28,7 @@ def make_gl_entries(
):
if gl_map:
if not cancel:
make_acc_dimensions_offsetting_entry(gl_map)
validate_accounting_period(gl_map)
validate_disabled_accounts(gl_map)
gl_map = process_gl_map(gl_map, merge_entries)
@@ -54,6 +52,63 @@ def make_gl_entries(
make_reverse_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
def make_acc_dimensions_offsetting_entry(gl_map):
accounting_dimensions_to_offset = get_accounting_dimensions_for_offsetting_entry(
gl_map, gl_map[0].company
)
no_of_dimensions = len(accounting_dimensions_to_offset)
if no_of_dimensions == 0:
return
offsetting_entries = []
for gle in gl_map:
for dimension in accounting_dimensions_to_offset:
offsetting_entry = gle.copy()
debit = flt(gle.credit) / no_of_dimensions if gle.credit != 0 else 0
credit = flt(gle.debit) / no_of_dimensions if gle.debit != 0 else 0
offsetting_entry.update(
{
"account": dimension.offsetting_account,
"debit": debit,
"credit": credit,
"debit_in_account_currency": debit,
"credit_in_account_currency": credit,
"remarks": _("Offsetting for Accounting Dimension") + " - {0}".format(dimension.name),
"against_voucher": None,
}
)
offsetting_entry["against_voucher_type"] = None
offsetting_entries.append(offsetting_entry)
gl_map += offsetting_entries
def get_accounting_dimensions_for_offsetting_entry(gl_map, company):
acc_dimension = frappe.qb.DocType("Accounting Dimension")
dimension_detail = frappe.qb.DocType("Accounting Dimension Detail")
acc_dimensions = (
frappe.qb.from_(acc_dimension)
.inner_join(dimension_detail)
.on(acc_dimension.name == dimension_detail.parent)
.select(acc_dimension.fieldname, acc_dimension.name, dimension_detail.offsetting_account)
.where(
(acc_dimension.disabled == 0)
& (dimension_detail.company == company)
& (dimension_detail.automatically_post_balancing_accounting_entry == 1)
)
).run(as_dict=True)
accounting_dimensions_to_offset = []
for acc_dimension in acc_dimensions:
values = set([entry.get(acc_dimension.fieldname) for entry in gl_map])
if len(values) > 1:
accounting_dimensions_to_offset.append(acc_dimension)
return accounting_dimensions_to_offset
def validate_disabled_accounts(gl_map):
accounts = [d.account for d in gl_map if d.account]
@@ -108,7 +163,8 @@ def process_gl_map(gl_map, merge_entries=True, precision=None):
if not gl_map:
return []
gl_map = distribute_gl_based_on_cost_center_allocation(gl_map, precision)
if gl_map[0].voucher_type != "Period Closing Voucher":
gl_map = distribute_gl_based_on_cost_center_allocation(gl_map, precision)
if merge_entries:
gl_map = merge_similar_entries(gl_map, precision)
@@ -300,6 +356,9 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
if gl_map:
check_freezing_date(gl_map[0]["posting_date"], adv_adj)
is_opening = any(d.get("is_opening") == "Yes" for d in gl_map)
if gl_map[0]["voucher_type"] != "Period Closing Voucher":
validate_against_pcv(is_opening, gl_map[0]["posting_date"], gl_map[0]["company"])
for entry in gl_map:
make_entry(entry, adv_adj, update_outstanding, from_repost)
@@ -521,6 +580,9 @@ def make_reverse_gl_entries(
)
validate_accounting_period(gl_entries)
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries)
validate_against_pcv(is_opening, gl_entries[0]["posting_date"], gl_entries[0]["company"])
set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
for entry in gl_entries:
@@ -568,6 +630,28 @@ def check_freezing_date(posting_date, adv_adj=False):
)
def validate_against_pcv(is_opening, posting_date, company):
if is_opening and frappe.db.exists(
"Period Closing Voucher", {"docstatus": 1, "company": company}
):
frappe.throw(
_("Opening Entry can not be created after Period Closing Voucher is created."),
title=_("Invalid Opening Entry"),
)
last_pcv_date = frappe.db.get_value(
"Period Closing Voucher", {"docstatus": 1, "company": company}, "max(posting_date)"
)
if last_pcv_date and getdate(posting_date) <= getdate(last_pcv_date):
message = _("Books have been closed till the period ending on {0}").format(
formatdate(last_pcv_date)
)
message += "</br >"
message += _("You cannot create/amend any accounting entries till this date.")
frappe.throw(message, title=_("Period Closed"))
def set_as_cancel(voucher_type, voucher_no):
"""
Set is_cancelled=1 in all original gl entries for the voucher

View File

@@ -14,6 +14,7 @@ from frappe.contacts.doctype.address.address import (
from frappe.contacts.doctype.contact.contact import get_contact_details
from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
from frappe.model.utils import get_fetch_values
from frappe.query_builder.functions import Abs, Date, Sum
from frappe.utils import (
add_days,
add_months,
@@ -33,6 +34,7 @@ import erpnext
from erpnext import get_company_currency
from erpnext.accounts.utils import get_fiscal_year
from erpnext.exceptions import InvalidAccountCurrency, PartyDisabled, PartyFrozen
from erpnext.utilities.regional import temporary_flag
PURCHASE_TRANSACTION_TYPES = {"Purchase Order", "Purchase Receipt", "Purchase Invoice"}
SALES_TRANSACTION_TYPES = {
@@ -261,9 +263,8 @@ def set_address_details(
)
if doctype in TRANSACTION_TYPES:
# required to set correct region
frappe.flags.company = company
get_regional_address_details(party_details, doctype, company)
with temporary_flag("company", company):
get_regional_address_details(party_details, doctype, company)
return party_address, shipping_address
@@ -885,30 +886,32 @@ def get_party_shipping_address(doctype: str, name: str) -> Optional[str]:
def get_partywise_advanced_payment_amount(
party_type, posting_date=None, future_payment=0, company=None, party=None
):
cond = "1=1"
ple = frappe.qb.DocType("Payment Ledger Entry")
query = (
frappe.qb.from_(ple)
.select(ple.party, Abs(Sum(ple.amount).as_("amount")))
.where(
(ple.party_type.isin(party_type))
& (ple.amount < 0)
& (ple.against_voucher_no == ple.voucher_no)
& (ple.delinked == 0)
)
.groupby(ple.party)
)
if posting_date:
if future_payment:
cond = "(posting_date <= '{0}' OR DATE(creation) <= '{0}')" "".format(posting_date)
query = query.where((ple.posting_date <= posting_date) | (Date(ple.creation) <= posting_date))
else:
cond = "posting_date <= '{0}'".format(posting_date)
query = query.where(ple.posting_date <= posting_date)
if company:
cond += "and company = {0}".format(frappe.db.escape(company))
query = query.where(ple.company == company)
if party:
cond += "and party = {0}".format(frappe.db.escape(party))
query = query.where(ple.party == party)
data = frappe.db.sql(
""" SELECT party, sum({0}) as amount
FROM `tabGL Entry`
WHERE
party_type = %s and against_voucher is null
and is_cancelled = 0
and {1} GROUP BY party""".format(
("credit") if party_type == "Customer" else "debit", cond
),
party_type,
)
data = query.run()
if data:
return frappe._dict(data)

View File

@@ -7,7 +7,7 @@ from erpnext.accounts.report.accounts_receivable.accounts_receivable import Rece
def execute(filters=None):
args = {
"party_type": "Supplier",
"account_type": "Payable",
"naming_by": ["Buying Settings", "supp_master_name"],
}
return ReceivablePayableReport(filters).run(args)

View File

@@ -9,7 +9,7 @@ from erpnext.accounts.report.accounts_receivable_summary.accounts_receivable_sum
def execute(filters=None):
args = {
"party_type": "Supplier",
"account_type": "Payable",
"naming_by": ["Buying Settings", "supp_master_name"],
}
return AccountsReceivableSummary(filters).run(args)

View File

@@ -7,7 +7,7 @@ from collections import OrderedDict
import frappe
from frappe import _, qb, scrub
from frappe.query_builder import Criterion
from frappe.query_builder.functions import Date
from frappe.query_builder.functions import Date, Sum
from frappe.utils import cint, cstr, flt, getdate, nowdate
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@@ -34,7 +34,7 @@ from erpnext.accounts.utils import get_currency_precision
def execute(filters=None):
args = {
"party_type": "Customer",
"account_type": "Receivable",
"naming_by": ["Selling Settings", "cust_master_name"],
}
return ReceivablePayableReport(filters).run(args)
@@ -70,8 +70,11 @@ class ReceivablePayableReport(object):
"Company", self.filters.get("company"), "default_currency"
)
self.currency_precision = get_currency_precision() or 2
self.dr_or_cr = "debit" if self.filters.party_type == "Customer" else "credit"
self.party_type = self.filters.party_type
self.dr_or_cr = "debit" if self.filters.account_type == "Receivable" else "credit"
self.account_type = self.filters.account_type
self.party_type = frappe.db.get_all(
"Party Type", {"account_type": self.account_type}, pluck="name"
)
self.party_details = {}
self.invoices = set()
self.skip_total_row = 0
@@ -197,6 +200,7 @@ class ReceivablePayableReport(object):
# no invoice, this is an invoice / stand-alone payment / credit note
row = self.voucher_balance.get((ple.voucher_type, ple.voucher_no, ple.party))
row.party_type = ple.party_type
return row
def update_voucher_balance(self, ple):
@@ -207,10 +211,11 @@ class ReceivablePayableReport(object):
return
# amount in "Party Currency", if its supplied. If not, amount in company currency
if self.filters.get(scrub(self.party_type)):
amount = ple.amount_in_account_currency
else:
amount = ple.amount
for party_type in self.party_type:
if self.filters.get(scrub(party_type)):
amount = ple.amount_in_account_currency
else:
amount = ple.amount
amount_in_account_currency = ple.amount_in_account_currency
# update voucher
@@ -362,7 +367,7 @@ class ReceivablePayableReport(object):
def get_invoice_details(self):
self.invoice_details = frappe._dict()
if self.party_type == "Customer":
if self.account_type == "Receivable":
si_list = frappe.db.sql(
"""
select name, due_date, po_no
@@ -390,7 +395,7 @@ class ReceivablePayableReport(object):
d.sales_person
)
if self.party_type == "Supplier":
if self.account_type == "Payable":
for pi in frappe.db.sql(
"""
select name, due_date, bill_no, bill_date
@@ -421,20 +426,21 @@ class ReceivablePayableReport(object):
# customer / supplier name
party_details = self.get_party_details(row.party) or {}
row.update(party_details)
if self.filters.get(scrub(self.filters.party_type)):
row.currency = row.account_currency
for party_type in self.party_type:
if self.filters.get(scrub(party_type)):
row.currency = row.account_currency
break
else:
row.currency = self.company_currency
def allocate_outstanding_based_on_payment_terms(self, row):
self.get_payment_terms(row)
for term in row.payment_terms:
# update "paid" and "oustanding" for this term
# update "paid" and "outstanding" for this term
if not term.paid:
self.allocate_closing_to_term(row, term, "paid")
# update "credit_note" and "oustanding" for this term
# update "credit_note" and "outstanding" for this term
if term.outstanding:
self.allocate_closing_to_term(row, term, "credit_note")
@@ -446,7 +452,8 @@ class ReceivablePayableReport(object):
"""
select
si.name, si.party_account_currency, si.currency, si.conversion_rate,
ps.due_date, ps.payment_term, ps.payment_amount, ps.description, ps.paid_amount, ps.discounted_amount
si.total_advance, ps.due_date, ps.payment_term, ps.payment_amount, ps.base_payment_amount,
ps.description, ps.paid_amount, ps.discounted_amount
from `tab{0}` si, `tabPayment Schedule` ps
where
si.name = ps.parent and
@@ -462,6 +469,10 @@ class ReceivablePayableReport(object):
original_row = frappe._dict(row)
row.payment_terms = []
# Advance allocated during invoicing is not considered in payment terms
# Deduct that from paid amount pre allocation
row.paid -= flt(payment_terms_details[0].total_advance)
# If no or single payment terms, no need to split the row
if len(payment_terms_details) <= 1:
return
@@ -476,7 +487,7 @@ class ReceivablePayableReport(object):
) and d.currency == d.party_account_currency:
invoiced = d.payment_amount
else:
invoiced = flt(flt(d.payment_amount) * flt(d.conversion_rate), self.currency_precision)
invoiced = d.base_payment_amount
row.payment_terms.append(
term.update(
@@ -532,65 +543,67 @@ class ReceivablePayableReport(object):
self.future_payments.setdefault((d.invoice_no, d.party), []).append(d)
def get_future_payments_from_payment_entry(self):
return frappe.db.sql(
"""
select
ref.reference_name as invoice_no,
payment_entry.party,
payment_entry.party_type,
payment_entry.posting_date as future_date,
ref.allocated_amount as future_amount,
payment_entry.reference_no as future_ref
from
`tabPayment Entry` as payment_entry inner join `tabPayment Entry Reference` as ref
on
(ref.parent = payment_entry.name)
where
payment_entry.docstatus < 2
and payment_entry.posting_date > %s
and payment_entry.party_type = %s
""",
(self.filters.report_date, self.party_type),
as_dict=1,
)
pe = frappe.qb.DocType("Payment Entry")
pe_ref = frappe.qb.DocType("Payment Entry Reference")
return (
frappe.qb.from_(pe)
.inner_join(pe_ref)
.on(pe_ref.parent == pe.name)
.select(
(pe_ref.reference_name).as_("invoice_no"),
pe.party,
pe.party_type,
(pe.posting_date).as_("future_date"),
(pe_ref.allocated_amount).as_("future_amount"),
(pe.reference_no).as_("future_ref"),
)
.where(
(pe.docstatus < 2)
& (pe.posting_date > self.filters.report_date)
& (pe.party_type.isin(self.party_type))
)
).run(as_dict=True)
def get_future_payments_from_journal_entry(self):
if self.filters.get("party"):
amount_field = (
"jea.debit_in_account_currency - jea.credit_in_account_currency"
if self.party_type == "Supplier"
else "jea.credit_in_account_currency - jea.debit_in_account_currency"
)
else:
amount_field = "jea.debit - " if self.party_type == "Supplier" else "jea.credit"
return frappe.db.sql(
"""
select
jea.reference_name as invoice_no,
je = frappe.qb.DocType("Journal Entry")
jea = frappe.qb.DocType("Journal Entry Account")
query = (
frappe.qb.from_(je)
.inner_join(jea)
.on(jea.parent == je.name)
.select(
jea.reference_name.as_("invoice_no"),
jea.party,
jea.party_type,
je.posting_date as future_date,
sum('{0}') as future_amount,
je.cheque_no as future_ref
from
`tabJournal Entry` as je inner join `tabJournal Entry Account` as jea
on
(jea.parent = je.name)
where
je.docstatus < 2
and je.posting_date > %s
and jea.party_type = %s
and jea.reference_name is not null and jea.reference_name != ''
group by je.name, jea.reference_name
having future_amount > 0
""".format(
amount_field
),
(self.filters.report_date, self.party_type),
as_dict=1,
je.posting_date.as_("future_date"),
je.cheque_no.as_("future_ref"),
)
.where(
(je.docstatus < 2)
& (je.posting_date > self.filters.report_date)
& (jea.party_type.isin(self.party_type))
& (jea.reference_name.isnotnull())
& (jea.reference_name != "")
)
)
if self.filters.get("party"):
if self.account_type == "Payable":
query = query.select(
Sum(jea.debit_in_account_currency - jea.credit_in_account_currency).as_("future_amount")
)
else:
query = query.select(
Sum(jea.credit_in_account_currency - jea.debit_in_account_currency).as_("future_amount")
)
else:
query = query.select(
Sum(jea.debit if self.account_type == "Payable" else jea.credit).as_("future_amount")
)
query = query.having(qb.Field("future_amount") > 0)
return query.run(as_dict=True)
def allocate_future_payments(self, row):
# future payments are captured in additional columns
# this method allocates pending future payments against a voucher to
@@ -619,13 +632,17 @@ class ReceivablePayableReport(object):
row.future_ref = ", ".join(row.future_ref)
def get_return_entries(self):
doctype = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice"
doctype = "Sales Invoice" if self.account_type == "Receivable" else "Purchase Invoice"
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)})
or_filters = {}
for party_type in self.party_type:
party_field = scrub(party_type)
if self.filters.get(party_field):
or_filters.update({party_field: self.filters.get(party_field)})
self.return_entries = frappe._dict(
frappe.get_all(doctype, filters, ["name", "return_against"], as_list=1)
frappe.get_all(
doctype, filters=filters, or_filters=or_filters, fields=["name", "return_against"], as_list=1
)
)
def set_ageing(self, row):
@@ -716,6 +733,7 @@ class ReceivablePayableReport(object):
)
.where(ple.delinked == 0)
.where(Criterion.all(self.qb_selection_filter))
.where(Criterion.any(self.or_filters))
)
if self.filters.get("group_by_party"):
@@ -746,16 +764,18 @@ class ReceivablePayableReport(object):
def prepare_conditions(self):
self.qb_selection_filter = []
party_type_field = scrub(self.party_type)
self.qb_selection_filter.append(self.ple.party_type == self.party_type)
self.or_filters = []
for party_type in self.party_type:
party_type_field = scrub(party_type)
self.or_filters.append(self.ple.party_type == party_type)
self.add_common_filters(party_type_field=party_type_field)
self.add_common_filters(party_type_field=party_type_field)
if party_type_field == "customer":
self.add_customer_filters()
if party_type_field == "customer":
self.add_customer_filters()
elif party_type_field == "supplier":
self.add_supplier_filters()
elif party_type_field == "supplier":
self.add_supplier_filters()
if self.filters.cost_center:
self.get_cost_center_conditions()
@@ -784,11 +804,10 @@ class ReceivablePayableReport(object):
self.qb_selection_filter.append(self.ple.account == self.filters.party_account)
else:
# get GL with "receivable" or "payable" account_type
account_type = "Receivable" if self.party_type == "Customer" else "Payable"
accounts = [
d.name
for d in frappe.get_all(
"Account", filters={"account_type": account_type, "company": self.filters.company}
"Account", filters={"account_type": self.account_type, "company": self.filters.company}
)
]
@@ -878,7 +897,7 @@ class ReceivablePayableReport(object):
def get_party_details(self, party):
if not party in self.party_details:
if self.party_type == "Customer":
if self.account_type == "Receivable":
fields = ["customer_name", "territory", "customer_group", "customer_primary_contact"]
if self.filters.get("sales_partner"):
@@ -901,14 +920,20 @@ class ReceivablePayableReport(object):
self.columns = []
self.add_column("Posting Date", fieldtype="Date")
self.add_column(
label=_(self.party_type),
label="Party Type",
fieldname="party_type",
fieldtype="Data",
width=100,
)
self.add_column(
label="Party",
fieldname="party",
fieldtype="Link",
options=self.party_type,
fieldtype="Dynamic Link",
options="party_type",
width=180,
)
self.add_column(
label="Receivable Account" if self.party_type == "Customer" else "Payable Account",
label=self.account_type + " Account",
fieldname="party_account",
fieldtype="Link",
options="Account",
@@ -916,13 +941,19 @@ class ReceivablePayableReport(object):
)
if self.party_naming_by == "Naming Series":
if self.account_type == "Payable":
label = "Supplier Name"
fieldname = "supplier_name"
else:
label = "Customer Name"
fieldname = "customer_name"
self.add_column(
_("{0} Name").format(self.party_type),
fieldname=scrub(self.party_type) + "_name",
label=label,
fieldname=fieldname,
fieldtype="Data",
)
if self.party_type == "Customer":
if self.account_type == "Receivable":
self.add_column(
_("Customer Contact"),
fieldname="customer_primary_contact",
@@ -942,7 +973,7 @@ class ReceivablePayableReport(object):
self.add_column(label="Due Date", fieldtype="Date")
if self.party_type == "Supplier":
if self.account_type == "Payable":
self.add_column(label=_("Bill No"), fieldname="bill_no", fieldtype="Data")
self.add_column(label=_("Bill Date"), fieldname="bill_date", fieldtype="Date")
@@ -952,7 +983,7 @@ class ReceivablePayableReport(object):
self.add_column(_("Invoiced Amount"), fieldname="invoiced")
self.add_column(_("Paid Amount"), fieldname="paid")
if self.party_type == "Customer":
if self.account_type == "Receivable":
self.add_column(_("Credit Note"), fieldname="credit_note")
else:
# note: fieldname is still `credit_note`
@@ -970,7 +1001,7 @@ class ReceivablePayableReport(object):
self.add_column(label=_("Future Payment Amount"), fieldname="future_amount")
self.add_column(label=_("Remaining Balance"), fieldname="remaining_balance")
if self.filters.party_type == "Customer":
if self.filters.account_type == "Receivable":
self.add_column(label=_("Customer LPO"), fieldname="po_no", fieldtype="Data")
# comma separated list of linked delivery notes
@@ -991,7 +1022,7 @@ class ReceivablePayableReport(object):
if self.filters.sales_partner:
self.add_column(label=_("Sales Partner"), fieldname="default_sales_partner", fieldtype="Data")
if self.filters.party_type == "Supplier":
if self.filters.account_type == "Payable":
self.add_column(
label=_("Supplier Group"),
fieldname="supplier_group",
@@ -1059,7 +1090,10 @@ class ReceivablePayableReport(object):
.where(
(je.company == self.filters.company)
& (je.posting_date.lte(self.filters.report_date))
& (je.voucher_type == "Exchange Rate Revaluation")
& (
(je.voucher_type == "Exchange Rate Revaluation")
| (je.voucher_type == "Exchange Gain Or Loss")
)
)
.run()
)

View File

@@ -12,7 +12,7 @@ from erpnext.accounts.report.accounts_receivable.accounts_receivable import Rece
def execute(filters=None):
args = {
"party_type": "Customer",
"account_type": "Receivable",
"naming_by": ["Selling Settings", "cust_master_name"],
}
@@ -21,7 +21,10 @@ def execute(filters=None):
class AccountsReceivableSummary(ReceivablePayableReport):
def run(self, args):
self.party_type = args.get("party_type")
self.account_type = args.get("account_type")
self.party_type = frappe.db.get_all(
"Party Type", {"account_type": self.account_type}, pluck="name"
)
self.party_naming_by = frappe.db.get_value(
args.get("naming_by")[0], None, args.get("naming_by")[1]
)
@@ -35,19 +38,24 @@ class AccountsReceivableSummary(ReceivablePayableReport):
self.get_party_total(args)
party = None
for party_type in self.party_type:
if self.filters.get(scrub(party_type)):
party = self.filters.get(scrub(party_type))
party_advance_amount = (
get_partywise_advanced_payment_amount(
self.party_type,
self.filters.report_date,
self.filters.show_future_payments,
self.filters.company,
party=self.filters.get(scrub(self.party_type)),
party=party,
)
or {}
)
if self.filters.show_gl_balance:
gl_balance_map = get_gl_balance(self.filters.report_date)
gl_balance_map = get_gl_balance(self.filters.report_date, self.filters.company)
for party, party_dict in self.party_total.items():
if party_dict.outstanding == 0:
@@ -57,9 +65,13 @@ class AccountsReceivableSummary(ReceivablePayableReport):
row.party = party
if self.party_naming_by == "Naming Series":
row.party_name = frappe.get_cached_value(
self.party_type, party, scrub(self.party_type) + "_name"
)
if self.account_type == "Payable":
doctype = "Supplier"
fieldname = "supplier_name"
else:
doctype = "Customer"
fieldname = "customer_name"
row.party_name = frappe.get_cached_value(doctype, party, fieldname)
row.update(party_dict)
@@ -93,6 +105,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
# set territory, customer_group, sales person etc
self.set_party_details(d)
self.party_total[d.party].update({"party_type": d.party_type})
def init_party_total(self, row):
self.party_total.setdefault(
@@ -131,17 +144,27 @@ class AccountsReceivableSummary(ReceivablePayableReport):
def get_columns(self):
self.columns = []
self.add_column(
label=_(self.party_type),
label=_("Party Type"),
fieldname="party_type",
fieldtype="Data",
width=100,
)
self.add_column(
label=_("Party"),
fieldname="party",
fieldtype="Link",
options=self.party_type,
fieldtype="Dynamic Link",
options="party_type",
width=180,
)
if self.party_naming_by == "Naming Series":
self.add_column(_("{0} Name").format(self.party_type), fieldname="party_name", fieldtype="Data")
self.add_column(
label=_("Supplier Name") if self.account_type == "Payable" else _("Customer Name"),
fieldname="party_name",
fieldtype="Data",
)
credit_debit_label = "Credit Note" if self.party_type == "Customer" else "Debit Note"
credit_debit_label = "Credit Note" if self.account_type == "Receivable" else "Debit Note"
self.add_column(_("Advance Amount"), fieldname="advance")
self.add_column(_("Invoiced Amount"), fieldname="invoiced")
@@ -159,7 +182,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
self.add_column(label=_("Future Payment Amount"), fieldname="future_amount")
self.add_column(label=_("Remaining Balance"), fieldname="remaining_balance")
if self.party_type == "Customer":
if self.account_type == "Receivable":
self.add_column(
label=_("Territory"), fieldname="territory", fieldtype="Link", options="Territory"
)
@@ -209,12 +232,12 @@ class AccountsReceivableSummary(ReceivablePayableReport):
self.add_column(label="Total Amount Due", fieldname="total_due")
def get_gl_balance(report_date):
def get_gl_balance(report_date, company):
return frappe._dict(
frappe.db.get_all(
"GL Entry",
fields=["party", "sum(debit - credit)"],
filters={"posting_date": ("<=", report_date), "is_cancelled": 0},
filters={"posting_date": ("<=", report_date), "is_cancelled": 0, "company": company},
group_by="party",
as_list=1,
)

View File

@@ -0,0 +1,203 @@
import unittest
import frappe
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import today
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.accounts_receivable_summary.accounts_receivable_summary import execute
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
def setUp(self):
self.maxDiff = None
self.create_company()
self.create_customer()
self.create_item()
self.clear_old_entries()
def tearDown(self):
frappe.db.rollback()
def test_01_receivable_summary_output(self):
"""
Test for Invoices, Paid, Advance and Outstanding
"""
filters = {
"company": self.company,
"customer": self.customer,
"posting_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
}
si = create_sales_invoice(
item=self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
posting_date=today(),
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
rate=200,
price_list_rate=200,
)
customer_group, customer_territory = frappe.db.get_all(
"Customer",
filters={"name": self.customer},
fields=["customer_group", "territory"],
as_list=True,
)[0]
report = execute(filters)
rpt_output = report[1]
expected_data = {
"party_type": "Customer",
"advance": 0,
"party": self.customer,
"invoiced": 200.0,
"paid": 0.0,
"credit_note": 0.0,
"outstanding": 200.0,
"range1": 200.0,
"range2": 0.0,
"range3": 0.0,
"range4": 0.0,
"range5": 0.0,
"total_due": 200.0,
"future_amount": 0.0,
"sales_person": [],
"currency": si.currency,
"territory": customer_territory,
"customer_group": customer_group,
}
self.assertEqual(len(rpt_output), 1)
self.assertDictEqual(rpt_output[0], expected_data)
# simulate advance payment
pe = get_payment_entry(si.doctype, si.name)
pe.paid_amount = 50
pe.references[0].allocated_amount = 0 # this essitially removes the reference
pe.save().submit()
# update expected data with advance
expected_data.update(
{
"advance": 50.0,
"outstanding": 150.0,
"range1": 150.0,
"total_due": 150.0,
}
)
report = execute(filters)
rpt_output = report[1]
self.assertEqual(len(rpt_output), 1)
self.assertDictEqual(rpt_output[0], expected_data)
# make partial payment
pe = get_payment_entry(si.doctype, si.name)
pe.paid_amount = 125
pe.references[0].allocated_amount = 125
pe.save().submit()
# update expected data after advance and partial payment
expected_data.update(
{"advance": 50.0, "paid": 125.0, "outstanding": 25.0, "range1": 25.0, "total_due": 25.0}
)
report = execute(filters)
rpt_output = report[1]
self.assertEqual(len(rpt_output), 1)
self.assertDictEqual(rpt_output[0], expected_data)
@change_settings("Selling Settings", {"cust_master_name": "Naming Series"})
def test_02_various_filters_and_output(self):
filters = {
"company": self.company,
"customer": self.customer,
"posting_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
}
si = create_sales_invoice(
item=self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
posting_date=today(),
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
rate=200,
price_list_rate=200,
)
# make partial payment
pe = get_payment_entry(si.doctype, si.name)
pe.paid_amount = 150
pe.references[0].allocated_amount = 150
pe.save().submit()
customer_group, customer_territory = frappe.db.get_all(
"Customer",
filters={"name": self.customer},
fields=["customer_group", "territory"],
as_list=True,
)[0]
report = execute(filters)
rpt_output = report[1]
expected_data = {
"party_type": "Customer",
"advance": 0,
"party": self.customer,
"party_name": self.customer,
"invoiced": 200.0,
"paid": 150.0,
"credit_note": 0.0,
"outstanding": 50.0,
"range1": 50.0,
"range2": 0.0,
"range3": 0.0,
"range4": 0.0,
"range5": 0.0,
"total_due": 50.0,
"future_amount": 0.0,
"sales_person": [],
"currency": si.currency,
"territory": customer_territory,
"customer_group": customer_group,
}
self.assertEqual(len(rpt_output), 1)
self.assertDictEqual(rpt_output[0], expected_data)
# with gl balance filter
filters.update({"show_gl_balance": True})
expected_data.update({"gl_balance": 50.0, "diff": 0.0})
report = execute(filters)
rpt_output = report[1]
self.assertEqual(len(rpt_output), 1)
self.assertDictEqual(rpt_output[0], expected_data)
# with gl balance and future payments filter
filters.update({"show_future_payments": True})
expected_data.update({"remaining_balance": 50.0})
report = execute(filters)
rpt_output = report[1]
self.assertEqual(len(rpt_output), 1)
self.assertDictEqual(rpt_output[0], expected_data)
# invoice fully paid
pe = get_payment_entry(si.doctype, si.name).save().submit()
report = execute(filters)
rpt_output = report[1]
self.assertEqual(len(rpt_output), 0)

View File

@@ -1,20 +1,23 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2016-04-08 14:49:58.133098",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 2,
"is_standard": "Yes",
"modified": "2017-02-24 20:08:26.084484",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Asset Depreciation Ledger",
"owner": "Administrator",
"ref_doctype": "Asset",
"report_name": "Asset Depreciation Ledger",
"report_type": "Script Report",
"add_total_row": 1,
"columns": [],
"creation": "2016-04-08 14:49:58.133098",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 2,
"is_standard": "Yes",
"letterhead": null,
"modified": "2023-07-26 21:05:33.554778",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Asset Depreciation Ledger",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Asset",
"report_name": "Asset Depreciation Ledger",
"report_type": "Script Report",
"roles": [
{
"role": "Accounts User"

View File

@@ -1,20 +1,23 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2016-04-08 14:56:37.235981",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 2,
"is_standard": "Yes",
"modified": "2017-02-24 20:08:18.660476",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Asset Depreciations and Balances",
"owner": "Administrator",
"ref_doctype": "Asset",
"report_name": "Asset Depreciations and Balances",
"report_type": "Script Report",
"add_total_row": 1,
"columns": [],
"creation": "2016-04-08 14:56:37.235981",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 2,
"is_standard": "Yes",
"letterhead": null,
"modified": "2023-07-26 21:04:54.751077",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Asset Depreciations and Balances",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Asset",
"report_name": "Asset Depreciations and Balances",
"report_type": "Script Report",
"roles": [
{
"role": "Accounts User"

View File

@@ -25,6 +25,8 @@ def execute(filters=None):
company=filters.company,
)
filters.period_start_date = period_list[0]["year_start_date"]
currency = filters.presentation_currency or frappe.get_cached_value(
"Company", filters.company, "default_currency"
)
@@ -96,7 +98,7 @@ def execute(filters=None):
chart = get_chart_data(filters, columns, asset, liability, equity)
report_summary = get_report_summary(
period_list, asset, liability, equity, provisional_profit_loss, total_credit, currency, filters
period_list, asset, liability, equity, provisional_profit_loss, currency, filters
)
return columns, data, message, chart, report_summary
@@ -174,7 +176,6 @@ def get_report_summary(
liability,
equity,
provisional_profit_loss,
total_credit,
currency,
filters,
consolidated=False,

View File

@@ -0,0 +1,51 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import today
from erpnext.accounts.report.balance_sheet.balance_sheet import execute
class TestBalanceSheet(FrappeTestCase):
def test_balance_sheet(self):
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import (
create_sales_invoice,
make_sales_invoice,
)
from erpnext.accounts.utils import get_fiscal_year
frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company 6'")
frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company 6'")
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 6'")
pi = make_purchase_invoice(
company="_Test Company 6",
warehouse="Finished Goods - _TC6",
expense_account="Cost of Goods Sold - _TC6",
cost_center="Main - _TC6",
qty=10,
rate=100,
)
si = create_sales_invoice(
company="_Test Company 6",
debit_to="Debtors - _TC6",
income_account="Sales - _TC6",
cost_center="Main - _TC6",
qty=5,
rate=110,
)
filters = frappe._dict(
company="_Test Company 6",
period_start_date=today(),
period_end_date=today(),
periodicity="Yearly",
)
result = execute(filters)[1]
for account_dict in result:
if account_dict.get("account") == "Current Liabilities - _TC6":
self.assertEqual(account_dict.total, 1000)
if account_dict.get("account") == "Current Assets - _TC6":
self.assertEqual(account_dict.total, 550)

View File

@@ -152,5 +152,5 @@ def get_entries(filters):
return sorted(
journal_entries + payment_entries + loan_disbursements + loan_repayments,
key=lambda k: k[2] or getdate(nowdate()),
key=lambda k: k[2].strftime("%H%M%S") or getdate(nowdate()),
)

View File

@@ -49,7 +49,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"label": __("Start Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1,
on_change: () => {
frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('from_fiscal_year'), function(r) {
@@ -65,7 +65,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"label": __("End Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1,
on_change: () => {
frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('to_fiscal_year'), function(r) {
@@ -139,7 +139,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
return value;
},
onload: function() {
let fiscal_year = frappe.defaults.get_user_default("fiscal_year")
let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today());
frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);

View File

@@ -6,6 +6,7 @@ from collections import defaultdict
import frappe
from frappe import _
from frappe.query_builder import Criterion
from frappe.utils import cint, flt, getdate
import erpnext
@@ -118,7 +119,6 @@ def get_balance_sheet_data(fiscal_year, companies, columns, filters):
liability,
equity,
provisional_profit_loss,
total_credit,
company_currency,
filters,
True,
@@ -365,6 +365,7 @@ def get_data(
accounts_by_name,
accounts,
ignore_closing_entries=False,
root_type=root_type,
)
calculate_values(accounts_by_name, gl_entries_by_account, companies, filters, fiscal_year)
@@ -609,6 +610,7 @@ def set_gl_entries_by_account(
accounts_by_name,
accounts,
ignore_closing_entries=False,
root_type=None,
):
"""Returns a dict like { "account": [gl entries], ... }"""
@@ -616,7 +618,6 @@ def set_gl_entries_by_account(
"Company", filters.get("company"), ["lft", "rgt"]
)
additional_conditions = get_additional_conditions(from_date, ignore_closing_entries, filters)
companies = frappe.db.sql(
""" select name, default_currency from `tabCompany`
where lft >= %(company_lft)s and rgt <= %(company_rgt)s""",
@@ -632,31 +633,47 @@ def set_gl_entries_by_account(
)
for d in companies:
gl_entries = frappe.db.sql(
"""select gl.posting_date, gl.account, gl.debit, gl.credit, gl.is_opening, gl.company,
gl.fiscal_year, gl.debit_in_account_currency, gl.credit_in_account_currency, gl.account_currency,
acc.account_name, acc.account_number
from `tabGL Entry` gl, `tabAccount` acc where acc.name = gl.account and gl.company = %(company)s and gl.is_cancelled = 0
{additional_conditions} and gl.posting_date <= %(to_date)s and acc.lft >= %(lft)s and acc.rgt <= %(rgt)s
order by gl.account, gl.posting_date""".format(
additional_conditions=additional_conditions
),
{
"from_date": from_date,
"to_date": to_date,
"lft": root_lft,
"rgt": root_rgt,
"company": d.name,
"finance_book": filters.get("finance_book"),
"company_fb": frappe.db.get_value("Company", d.name, "default_finance_book"),
},
as_dict=True,
gle = frappe.qb.DocType("GL Entry")
account = frappe.qb.DocType("Account")
query = (
frappe.qb.from_(gle)
.inner_join(account)
.on(account.name == gle.account)
.select(
gle.posting_date,
gle.account,
gle.debit,
gle.credit,
gle.is_opening,
gle.company,
gle.fiscal_year,
gle.debit_in_account_currency,
gle.credit_in_account_currency,
gle.account_currency,
account.account_name,
account.account_number,
)
.where(
(gle.company == d.name)
& (gle.is_cancelled == 0)
& (gle.posting_date <= to_date)
& (account.lft >= root_lft)
& (account.rgt <= root_rgt)
)
.orderby(gle.account, gle.posting_date)
)
if root_type:
query = query.where(account.root_type == root_type)
additional_conditions = get_additional_conditions(from_date, ignore_closing_entries, filters, d)
if additional_conditions:
query = query.where(Criterion.all(additional_conditions))
gl_entries = query.run(as_dict=True)
if filters and filters.get("presentation_currency") != d.default_currency:
currency_info["company"] = d.name
currency_info["company_currency"] = d.default_currency
convert_to_presentation_currency(gl_entries, currency_info, filters.get("company"))
convert_to_presentation_currency(gl_entries, currency_info)
for entry in gl_entries:
if entry.account_number:
@@ -722,23 +739,30 @@ def validate_entries(key, entry, accounts_by_name, accounts):
accounts.insert(idx + 1, args)
def get_additional_conditions(from_date, ignore_closing_entries, filters):
def get_additional_conditions(from_date, ignore_closing_entries, filters, d):
gle = frappe.qb.DocType("GL Entry")
additional_conditions = []
if ignore_closing_entries:
additional_conditions.append("ifnull(gl.voucher_type, '')!='Period Closing Voucher'")
additional_conditions.append((gle.voucher_type != "Period Closing Voucher"))
if from_date:
additional_conditions.append("gl.posting_date >= %(from_date)s")
additional_conditions.append(gle.posting_date >= from_date)
finance_books = []
finance_books.append("")
if filter_fb := filters.get("finance_book"):
finance_books.append(filter_fb)
if filters.get("include_default_book_entries"):
additional_conditions.append(
"(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)"
)
else:
additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)")
if company_fb := frappe.get_cached_value("Company", d.name, "default_finance_book"):
finance_books.append(company_fb)
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
additional_conditions.append((gle.finance_book.isin(finance_books)) | gle.finance_book.isnull())
else:
additional_conditions.append((gle.finance_book.isin(finance_books)) | gle.finance_book.isnull())
return additional_conditions
def add_total_row(out, root_type, balance_must_be, companies, company_currency):

View File

@@ -48,7 +48,7 @@ function get_filters() {
"label": __("Start Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1
},
{
@@ -56,7 +56,7 @@ function get_filters() {
"label": __("End Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1
},
{
@@ -100,7 +100,7 @@ frappe.query_reports["Deferred Revenue and Expense"] = {
return default_formatter(value, row, column, data);
},
onload: function(report){
let fiscal_year = frappe.defaults.get_user_default("fiscal_year");
let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today());
frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);

View File

@@ -4,9 +4,10 @@
import frappe
from frappe import _, qb
from frappe.query_builder import Column, functions
from frappe.utils import add_days, date_diff, flt, get_first_day, get_last_day, rounded
from frappe.utils import add_days, date_diff, flt, get_first_day, get_last_day, getdate, rounded
from erpnext.accounts.report.financial_statements import get_period_list
from erpnext.accounts.utils import get_fiscal_year
class Deferred_Item(object):
@@ -226,7 +227,7 @@ class Deferred_Revenue_and_Expense_Report(object):
# If no filters are provided, get user defaults
if not filters:
fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date=getdate()))
self.filters = frappe._dict(
{
"company": frappe.defaults.get_user_default("Company"),

View File

@@ -2,6 +2,7 @@ import unittest
import frappe
from frappe import qb
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import nowdate
from erpnext.accounts.doctype.account.test_account import create_account
@@ -10,15 +11,15 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal
from erpnext.accounts.report.deferred_revenue_and_expense.deferred_revenue_and_expense import (
Deferred_Revenue_and_Expense_Report,
)
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
from erpnext.stock.doctype.item.test_item import create_item
class TestDeferredRevenueAndExpense(unittest.TestCase):
class TestDeferredRevenueAndExpense(FrappeTestCase, AccountsTestMixin):
@classmethod
def setUpClass(self):
clear_accounts_and_items()
create_company()
self.maxDiff = None
def clear_old_entries(self):
@@ -50,55 +51,58 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
if deferred_invoices:
qb.from_(pinv).delete().where(pinv.name.isin(deferred_invoices)).run()
def test_deferred_revenue(self):
self.clear_old_entries()
def setup_deferred_accounts_and_items(self):
# created deferred expense accounts, if not found
self.deferred_revenue_account = create_account(
account_name="Deferred Revenue",
parent_account="Current Liabilities - " + self.company_abbr,
company=self.company,
)
# created deferred expense accounts, if not found
deferred_revenue_account = create_account(
account_name="Deferred Revenue",
parent_account="Current Liabilities - _CD",
company="_Test Company DR",
self.deferred_expense_account = create_account(
account_name="Deferred Expense",
parent_account="Current Assets - " + self.company_abbr,
company=self.company,
)
acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
acc_settings.book_deferred_entries_based_on = "Months"
acc_settings.save()
def setUp(self):
self.create_company()
self.create_customer("_Test Customer")
self.create_supplier("_Test Furniture Supplier")
self.setup_deferred_accounts_and_items()
self.clear_old_entries()
customer = frappe.new_doc("Customer")
customer.customer_name = "_Test Customer DR"
customer.type = "Individual"
customer.insert()
def tearDown(self):
frappe.db.rollback()
item = create_item(
"_Test Internet Subscription",
is_stock_item=0,
warehouse="All Warehouses - _CD",
company="_Test Company DR",
)
@change_settings("Accounts Settings", {"book_deferred_entries_based_on": "Months"})
def test_deferred_revenue(self):
self.create_item("_Test Internet Subscription", 0, self.warehouse, self.company)
item = frappe.get_doc("Item", self.item)
item.enable_deferred_revenue = 1
item.deferred_revenue_account = deferred_revenue_account
item.deferred_revenue_account = self.deferred_revenue_account
item.no_of_months = 3
item.save()
si = create_sales_invoice(
item=item.name,
company="_Test Company DR",
customer="_Test Customer DR",
debit_to="Debtors - _CD",
item=self.item,
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
posting_date="2021-05-01",
parent_cost_center="Main - _CD",
cost_center="Main - _CD",
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
do_not_save=True,
rate=300,
price_list_rate=300,
)
si.items[0].income_account = "Sales - _CD"
si.items[0].income_account = self.income_account
si.items[0].enable_deferred_revenue = 1
si.items[0].service_start_date = "2021-05-01"
si.items[0].service_end_date = "2021-08-01"
si.items[0].deferred_revenue_account = deferred_revenue_account
si.items[0].income_account = "Sales - _CD"
si.items[0].deferred_revenue_account = self.deferred_revenue_account
si.save()
si.submit()
@@ -109,17 +113,17 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
start_date="2021-05-01",
end_date="2021-08-01",
type="Income",
company="_Test Company DR",
company=self.company,
)
)
pda.insert()
pda.submit()
# execute report
fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
self.filters = frappe._dict(
{
"company": frappe.defaults.get_user_default("Company"),
"company": self.company,
"filter_based_on": "Date Range",
"period_start_date": "2021-05-01",
"period_end_date": "2021-08-01",
@@ -141,57 +145,36 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
]
self.assertEqual(report.period_total, expected)
@change_settings("Accounts Settings", {"book_deferred_entries_based_on": "Months"})
def test_deferred_expense(self):
self.clear_old_entries()
# created deferred expense accounts, if not found
deferred_expense_account = create_account(
account_name="Deferred Expense",
parent_account="Current Assets - _CD",
company="_Test Company DR",
)
acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
acc_settings.book_deferred_entries_based_on = "Months"
acc_settings.save()
supplier = create_supplier(
supplier_name="_Test Furniture Supplier", supplier_group="Local", supplier_type="Company"
)
supplier.save()
item = create_item(
"_Test Office Desk",
is_stock_item=0,
warehouse="All Warehouses - _CD",
company="_Test Company DR",
)
self.create_item("_Test Office Desk", 0, self.warehouse, self.company)
item = frappe.get_doc("Item", self.item)
item.enable_deferred_expense = 1
item.deferred_expense_account = deferred_expense_account
item.deferred_expense_account = self.deferred_expense_account
item.no_of_months_exp = 3
item.save()
pi = make_purchase_invoice(
item=item.name,
company="_Test Company DR",
supplier="_Test Furniture Supplier",
item=self.item,
company=self.company,
supplier=self.supplier,
is_return=False,
update_stock=False,
posting_date=frappe.utils.datetime.date(2021, 5, 1),
parent_cost_center="Main - _CD",
cost_center="Main - _CD",
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
do_not_save=True,
rate=300,
price_list_rate=300,
warehouse="All Warehouses - _CD",
warehouse=self.warehouse,
qty=1,
)
pi.set_posting_time = True
pi.items[0].enable_deferred_expense = 1
pi.items[0].service_start_date = "2021-05-01"
pi.items[0].service_end_date = "2021-08-01"
pi.items[0].deferred_expense_account = deferred_expense_account
pi.items[0].expense_account = "Office Maintenance Expenses - _CD"
pi.items[0].deferred_expense_account = self.deferred_expense_account
pi.items[0].expense_account = self.expense_account
pi.save()
pi.submit()
@@ -202,17 +185,17 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
start_date="2021-05-01",
end_date="2021-08-01",
type="Expense",
company="_Test Company DR",
company=self.company,
)
)
pda.insert()
pda.submit()
# execute report
fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
self.filters = frappe._dict(
{
"company": frappe.defaults.get_user_default("Company"),
"company": self.company,
"filter_based_on": "Date Range",
"period_start_date": "2021-05-01",
"period_end_date": "2021-08-01",
@@ -234,52 +217,31 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
]
self.assertEqual(report.period_total, expected)
@change_settings("Accounts Settings", {"book_deferred_entries_based_on": "Months"})
def test_zero_months(self):
self.clear_old_entries()
# created deferred expense accounts, if not found
deferred_revenue_account = create_account(
account_name="Deferred Revenue",
parent_account="Current Liabilities - _CD",
company="_Test Company DR",
)
acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
acc_settings.book_deferred_entries_based_on = "Months"
acc_settings.save()
customer = frappe.new_doc("Customer")
customer.customer_name = "_Test Customer DR"
customer.type = "Individual"
customer.insert()
item = create_item(
"_Test Internet Subscription",
is_stock_item=0,
warehouse="All Warehouses - _CD",
company="_Test Company DR",
)
self.create_item("_Test Internet Subscription", 0, self.warehouse, self.company)
item = frappe.get_doc("Item", self.item)
item.enable_deferred_revenue = 1
item.deferred_revenue_account = deferred_revenue_account
item.deferred_revenue_account = self.deferred_revenue_account
item.no_of_months = 0
item.save()
si = create_sales_invoice(
item=item.name,
company="_Test Company DR",
customer="_Test Customer DR",
debit_to="Debtors - _CD",
company=self.company,
customer=self.customer,
debit_to=self.debit_to,
posting_date="2021-05-01",
parent_cost_center="Main - _CD",
cost_center="Main - _CD",
parent_cost_center=self.cost_center,
cost_center=self.cost_center,
do_not_save=True,
rate=300,
price_list_rate=300,
)
si.items[0].enable_deferred_revenue = 1
si.items[0].income_account = "Sales - _CD"
si.items[0].deferred_revenue_account = deferred_revenue_account
si.items[0].income_account = "Sales - _CD"
si.items[0].income_account = self.income_account
si.items[0].deferred_revenue_account = self.deferred_revenue_account
si.save()
si.submit()
@@ -290,17 +252,17 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
start_date="2021-05-01",
end_date="2021-08-01",
type="Income",
company="_Test Company DR",
company=self.company,
)
)
pda.insert()
pda.submit()
# execute report
fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year"))
fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
self.filters = frappe._dict(
{
"company": frappe.defaults.get_user_default("Company"),
"company": self.company,
"filter_based_on": "Date Range",
"period_start_date": "2021-05-01",
"period_end_date": "2021-08-01",
@@ -321,30 +283,3 @@ class TestDeferredRevenueAndExpense(unittest.TestCase):
{"key": "aug_2021", "total": 0, "actual": 0},
]
self.assertEqual(report.period_total, expected)
def create_company():
company = frappe.db.exists("Company", "_Test Company DR")
if not company:
company = frappe.new_doc("Company")
company.company_name = "_Test Company DR"
company.default_currency = "INR"
company.chart_of_accounts = "Standard"
company.insert()
def clear_accounts_and_items():
item = qb.DocType("Item")
account = qb.DocType("Account")
customer = qb.DocType("Customer")
supplier = qb.DocType("Supplier")
qb.from_(account).delete().where(
(account.account_name == "Deferred Revenue")
| (account.account_name == "Deferred Expense") & (account.company == "_Test Company DR")
).run()
qb.from_(item).delete().where(
(item.item_code == "_Test Internet Subscription") | (item.item_code == "_Test Office Rent")
).run()
qb.from_(customer).delete().where(customer.customer_name == "_Test Customer DR").run()
qb.from_(supplier).delete().where(supplier.supplier_name == "_Test Furniture Supplier").run()

View File

@@ -18,7 +18,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1,
"on_change": function(query_report) {
var fiscal_year = query_report.get_values().fiscal_year;

View File

@@ -188,6 +188,7 @@ def get_data(
filters,
gl_entries_by_account,
ignore_closing_entries=ignore_closing_entries,
root_type=root_type,
)
calculate_values(
@@ -334,12 +335,10 @@ def add_total_row(out, root_type, balance_must_be, period_list, company_currency
for period in period_list:
total_row.setdefault(period.key, 0.0)
total_row[period.key] += row.get(period.key, 0.0)
row[period.key] = row.get(period.key, 0.0)
total_row.setdefault("total", 0.0)
total_row["total"] += flt(row["total"])
total_row["opening_balance"] += row["opening_balance"]
row["total"] = ""
if "total" in total_row:
out.append(total_row)
@@ -416,52 +415,74 @@ def set_gl_entries_by_account(
filters,
gl_entries_by_account,
ignore_closing_entries=False,
ignore_opening_entries=False,
root_type=None,
):
"""Returns a dict like { "account": [gl entries], ... }"""
gl_entries = []
additional_conditions = get_additional_conditions(from_date, ignore_closing_entries, filters)
account_filters = {
"company": company,
"is_group": 0,
"lft": (">=", root_lft),
"rgt": ("<=", root_rgt),
}
accounts = frappe.db.sql_list(
"""select name from `tabAccount`
where lft >= %s and rgt <= %s and company = %s""",
(root_lft, root_rgt, company),
)
if accounts:
additional_conditions += " and account in ({})".format(
", ".join(frappe.db.escape(d) for d in accounts)
if root_type:
account_filters.update(
{
"root_type": root_type,
}
)
gl_filters = {
"company": company,
"from_date": from_date,
"to_date": to_date,
"finance_book": cstr(filters.get("finance_book")),
}
accounts_list = frappe.db.get_all(
"Account",
filters=account_filters,
pluck="name",
)
if filters.get("include_default_book_entries"):
gl_filters["company_fb"] = frappe.db.get_value("Company", company, "default_finance_book")
if accounts_list:
# For balance sheet
ignore_closing_balances = frappe.db.get_single_value(
"Accounts Settings", "ignore_account_closing_balance"
)
if not from_date and not ignore_closing_balances:
last_period_closing_voucher = frappe.db.get_all(
"Period Closing Voucher",
filters={
"docstatus": 1,
"company": filters.company,
"posting_date": ("<", filters["period_start_date"]),
},
fields=["posting_date", "name"],
order_by="posting_date desc",
limit=1,
)
if last_period_closing_voucher:
gl_entries += get_accounting_entries(
"Account Closing Balance",
from_date,
to_date,
accounts_list,
filters,
ignore_closing_entries,
last_period_closing_voucher[0].name,
)
from_date = add_days(last_period_closing_voucher[0].posting_date, 1)
ignore_opening_entries = True
for key, value in filters.items():
if value:
gl_filters.update({key: value})
gl_entries = frappe.db.sql(
"""
select posting_date, account, debit, credit, is_opening, fiscal_year,
debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry`
where company=%(company)s
{additional_conditions}
and posting_date <= %(to_date)s
and is_cancelled = 0""".format(
additional_conditions=additional_conditions
),
gl_filters,
as_dict=True,
gl_entries += get_accounting_entries(
"GL Entry",
from_date,
to_date,
accounts_list,
filters,
ignore_closing_entries,
ignore_opening_entries=ignore_opening_entries,
)
if filters and filters.get("presentation_currency"):
convert_to_presentation_currency(gl_entries, get_currency(filters), filters.get("company"))
convert_to_presentation_currency(gl_entries, get_currency(filters))
for entry in gl_entries:
gl_entries_by_account.setdefault(entry.account, []).append(entry)
@@ -469,49 +490,90 @@ def set_gl_entries_by_account(
return gl_entries_by_account
def get_additional_conditions(from_date, ignore_closing_entries, filters):
additional_conditions = []
def get_accounting_entries(
doctype,
from_date,
to_date,
accounts,
filters,
ignore_closing_entries,
period_closing_voucher=None,
ignore_opening_entries=False,
):
gl_entry = frappe.qb.DocType(doctype)
query = (
frappe.qb.from_(gl_entry)
.select(
gl_entry.account,
gl_entry.debit,
gl_entry.credit,
gl_entry.debit_in_account_currency,
gl_entry.credit_in_account_currency,
gl_entry.account_currency,
)
.where(gl_entry.company == filters.company)
)
if doctype == "GL Entry":
query = query.select(gl_entry.posting_date, gl_entry.is_opening, gl_entry.fiscal_year)
query = query.where(gl_entry.is_cancelled == 0)
query = query.where(gl_entry.posting_date <= to_date)
if ignore_opening_entries:
query = query.where(gl_entry.is_opening == "No")
else:
query = query.select(gl_entry.closing_date.as_("posting_date"))
query = query.where(gl_entry.period_closing_voucher == period_closing_voucher)
query = apply_additional_conditions(doctype, query, from_date, ignore_closing_entries, filters)
query = query.where(gl_entry.account.isin(accounts))
entries = query.run(as_dict=True)
return entries
def apply_additional_conditions(doctype, query, from_date, ignore_closing_entries, filters):
gl_entry = frappe.qb.DocType(doctype)
accounting_dimensions = get_accounting_dimensions(as_list=False)
if ignore_closing_entries:
additional_conditions.append("ifnull(voucher_type, '')!='Period Closing Voucher'")
if doctype == "GL Entry":
query = query.where(gl_entry.voucher_type != "Period Closing Voucher")
else:
query = query.where(gl_entry.is_period_closing_voucher_entry == 0)
if from_date:
additional_conditions.append("posting_date >= %(from_date)s")
if from_date and doctype == "GL Entry":
query = query.where(gl_entry.posting_date >= from_date)
if filters:
if filters.get("project"):
if not isinstance(filters.get("project"), list):
filters.project = frappe.parse_json(filters.get("project"))
additional_conditions.append("project in %(project)s")
query = query.where(gl_entry.project.isin(filters.project))
if filters.get("cost_center"):
filters.cost_center = get_cost_centers_with_children(filters.cost_center)
additional_conditions.append("cost_center in %(cost_center)s")
query = query.where(gl_entry.cost_center.isin(filters.cost_center))
if filters.get("include_default_book_entries"):
if filters.get("finance_book"):
if filters.get("company_fb") and cstr(filters.get("finance_book")) != cstr(
filters.get("company_fb")
):
frappe.throw(
_("To use a different finance book, please uncheck 'Include Default Book Entries'")
)
else:
additional_conditions.append(
"(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)"
)
else:
additional_conditions.append("(finance_book in (%(company_fb)s, '') OR finance_book IS NULL)")
else:
if filters.get("finance_book"):
additional_conditions.append(
"(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)"
company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book")
if filters.finance_book and company_fb and cstr(filters.finance_book) != cstr(company_fb):
frappe.throw(
_("To use a different finance book, please uncheck 'Include Default Book Entries'")
)
else:
additional_conditions.append("(finance_book in ('') OR finance_book IS NULL)")
query = query.where(
(gl_entry.finance_book.isin([cstr(filters.finance_book), cstr(company_fb), ""]))
| (gl_entry.finance_book.isnull())
)
else:
query = query.where(
(gl_entry.finance_book.isin([cstr(filters.finance_book), ""]))
| (gl_entry.finance_book.isnull())
)
if accounting_dimensions:
for dimension in accounting_dimensions:
@@ -520,11 +582,10 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters):
filters[dimension.fieldname] = get_dimension_with_children(
dimension.document_type, filters.get(dimension.fieldname)
)
additional_conditions.append("{0} in %({0})s".format(dimension.fieldname))
else:
additional_conditions.append("{0} in %({0})s".format(dimension.fieldname))
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
query = query.where(gl_entry[dimension.fieldname].isin(filters[dimension.fieldname]))
return query
def get_cost_centers_with_children(cost_centers):
@@ -576,7 +637,13 @@ def get_columns(periodicity, period_list, accumulated_values=1, company=None):
if periodicity != "Yearly":
if not accumulated_values:
columns.append(
{"fieldname": "total", "label": _("Total"), "fieldtype": "Currency", "width": 150}
{
"fieldname": "total",
"label": _("Total"),
"fieldtype": "Currency",
"width": 150,
"options": "currency",
}
)
return columns

View File

@@ -0,0 +1,52 @@
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
function get_filters() {
let filters = [
{
"fieldname":"company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"default": frappe.defaults.get_user_default("Company"),
"reqd": 1
},
{
"fieldname":"period_start_date",
"label": __("Start Date"),
"fieldtype": "Date",
"reqd": 1,
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1)
},
{
"fieldname":"period_end_date",
"label": __("End Date"),
"fieldtype": "Date",
"reqd": 1,
"default": frappe.datetime.get_today()
},
{
"fieldname":"account",
"label": __("Account"),
"fieldtype": "MultiSelectList",
"options": "Account",
get_data: function(txt) {
return frappe.db.get_link_options('Account', txt, {
company: frappe.query_report.get_filter_value("company"),
account_type: ['in', ["Receivable", "Payable"]]
});
}
},
{
"fieldname":"voucher_no",
"label": __("Voucher No"),
"fieldtype": "Data",
"width": 100,
},
]
return filters;
}
frappe.query_reports["General and Payment Ledger Comparison"] = {
"filters": get_filters()
};

View File

@@ -0,0 +1,32 @@
{
"add_total_row": 0,
"columns": [],
"creation": "2023-08-02 17:30:29.494907",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 0,
"is_standard": "Yes",
"letterhead": null,
"modified": "2023-08-02 17:30:29.494907",
"modified_by": "Administrator",
"module": "Accounts",
"name": "General and Payment Ledger Comparison",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "GL Entry",
"report_name": "General and Payment Ledger Comparison",
"report_type": "Script Report",
"roles": [
{
"role": "Accounts User"
},
{
"role": "Accounts Manager"
},
{
"role": "Auditor"
}
]
}

View File

@@ -0,0 +1,221 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe import _, qb
from frappe.query_builder import Criterion
from frappe.query_builder.functions import Sum
class General_Payment_Ledger_Comparison(object):
"""
A Utility report to compare Voucher-wise balance between General and Payment Ledger
"""
def __init__(self, filters=None):
self.filters = filters
self.gle = []
self.ple = []
def get_accounts(self):
receivable_accounts = [
x[0]
for x in frappe.db.get_all(
"Account",
filters={"company": self.filters.company, "account_type": "Receivable"},
as_list=True,
)
]
payable_accounts = [
x[0]
for x in frappe.db.get_all(
"Account", filters={"company": self.filters.company, "account_type": "Payable"}, as_list=True
)
]
self.account_types = frappe._dict(
{
"receivable": frappe._dict({"accounts": receivable_accounts, "gle": [], "ple": []}),
"payable": frappe._dict({"accounts": payable_accounts, "gle": [], "ple": []}),
}
)
def generate_filters(self):
if self.filters.account:
self.account_types.receivable.accounts = []
self.account_types.payable.accounts = []
for acc in frappe.db.get_all(
"Account", filters={"name": ["in", self.filters.account]}, fields=["name", "account_type"]
):
if acc.account_type == "Receivable":
self.account_types.receivable.accounts.append(acc.name)
else:
self.account_types.payable.accounts.append(acc.name)
def get_gle(self):
gle = qb.DocType("GL Entry")
for acc_type, val in self.account_types.items():
if val.accounts:
filter_criterion = []
if self.filters.voucher_no:
filter_criterion.append((gle.voucher_no == self.filters.voucher_no))
if self.filters.period_start_date:
filter_criterion.append(gle.posting_date.gte(self.filters.period_start_date))
if self.filters.period_end_date:
filter_criterion.append(gle.posting_date.lte(self.filters.period_end_date))
if acc_type == "receivable":
outstanding = (Sum(gle.debit) - Sum(gle.credit)).as_("outstanding")
else:
outstanding = (Sum(gle.credit) - Sum(gle.debit)).as_("outstanding")
self.account_types[acc_type].gle = (
qb.from_(gle)
.select(
gle.company,
gle.account,
gle.voucher_no,
gle.party,
outstanding,
)
.where(
(gle.company == self.filters.company)
& (gle.is_cancelled == 0)
& (gle.account.isin(val.accounts))
)
.where(Criterion.all(filter_criterion))
.groupby(gle.company, gle.account, gle.voucher_no, gle.party)
.run()
)
def get_ple(self):
ple = qb.DocType("Payment Ledger Entry")
for acc_type, val in self.account_types.items():
if val.accounts:
filter_criterion = []
if self.filters.voucher_no:
filter_criterion.append((ple.voucher_no == self.filters.voucher_no))
if self.filters.period_start_date:
filter_criterion.append(ple.posting_date.gte(self.filters.period_start_date))
if self.filters.period_end_date:
filter_criterion.append(ple.posting_date.lte(self.filters.period_end_date))
self.account_types[acc_type].ple = (
qb.from_(ple)
.select(
ple.company, ple.account, ple.voucher_no, ple.party, Sum(ple.amount).as_("outstanding")
)
.where(
(ple.company == self.filters.company)
& (ple.delinked == 0)
& (ple.account.isin(val.accounts))
)
.where(Criterion.all(filter_criterion))
.groupby(ple.company, ple.account, ple.voucher_no, ple.party)
.run()
)
def compare(self):
self.gle_balances = set()
self.ple_balances = set()
# consolidate both receivable and payable balances in one set
for acc_type, val in self.account_types.items():
self.gle_balances = set(val.gle) | self.gle_balances
self.ple_balances = set(val.ple) | self.ple_balances
self.diff1 = self.gle_balances.difference(self.ple_balances)
self.diff2 = self.ple_balances.difference(self.gle_balances)
self.diff = frappe._dict({})
for x in self.diff1:
self.diff[(x[0], x[1], x[2], x[3])] = frappe._dict({"gl_balance": x[4]})
for x in self.diff2:
self.diff[(x[0], x[1], x[2], x[3])].update(frappe._dict({"pl_balance": x[4]}))
def generate_data(self):
self.data = []
for key, val in self.diff.items():
self.data.append(
frappe._dict(
{
"voucher_no": key[2],
"party": key[3],
"gl_balance": val.gl_balance,
"pl_balance": val.pl_balance,
}
)
)
def get_columns(self):
self.columns = []
options = None
self.columns.append(
dict(
label=_("Voucher No"),
fieldname="voucher_no",
fieldtype="Data",
options=options,
width="100",
)
)
self.columns.append(
dict(
label=_("Party"),
fieldname="party",
fieldtype="Data",
options=options,
width="100",
)
)
self.columns.append(
dict(
label=_("GL Balance"),
fieldname="gl_balance",
fieldtype="Currency",
options="Company:company:default_currency",
width="100",
)
)
self.columns.append(
dict(
label=_("Payment Ledger Balance"),
fieldname="pl_balance",
fieldtype="Currency",
options="Company:company:default_currency",
width="100",
)
)
def run(self):
self.get_accounts()
self.generate_filters()
self.get_gle()
self.get_ple()
self.compare()
self.generate_data()
self.get_columns()
return self.columns, self.data
def execute(filters=None):
columns, data = [], []
rpt = General_Payment_Ledger_Comparison(filters)
columns, data = rpt.run()
return columns, data

View File

@@ -0,0 +1,100 @@
import unittest
import frappe
from frappe import qb
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.general_and_payment_ledger_comparison.general_and_payment_ledger_comparison import (
execute,
)
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
class TestGeneralAndPaymentLedger(FrappeTestCase, AccountsTestMixin):
def setUp(self):
self.create_company()
self.cleanup()
def tearDown(self):
frappe.db.rollback()
def cleanup(self):
doctypes = []
doctypes.append(qb.DocType("GL Entry"))
doctypes.append(qb.DocType("Payment Ledger Entry"))
doctypes.append(qb.DocType("Sales Invoice"))
for doctype in doctypes:
qb.from_(doctype).delete().where(doctype.company == self.company).run()
def test_01_basic_report_functionality(self):
sinv = create_sales_invoice(
company=self.company,
debit_to=self.debit_to,
expense_account=self.expense_account,
cost_center=self.cost_center,
income_account=self.income_account,
warehouse=self.warehouse,
)
# manually edit the payment ledger entry
ple = frappe.db.get_all(
"Payment Ledger Entry", filters={"voucher_no": sinv.name, "delinked": 0}
)[0]
frappe.db.set_value("Payment Ledger Entry", ple.name, "amount", sinv.grand_total - 1)
filters = frappe._dict({"company": self.company})
columns, data = execute(filters=filters)
self.assertEqual(len(data), 1)
expected = {
"voucher_no": sinv.name,
"party": sinv.customer,
"gl_balance": sinv.grand_total,
"pl_balance": sinv.grand_total - 1,
}
self.assertEqual(expected, data[0])
# account filter
filters = frappe._dict({"company": self.company, "account": self.debit_to})
columns, data = execute(filters=filters)
self.assertEqual(len(data), 1)
self.assertEqual(expected, data[0])
filters = frappe._dict({"company": self.company, "account": self.creditors})
columns, data = execute(filters=filters)
self.assertEqual([], data)
# voucher_no filter
filters = frappe._dict({"company": self.company, "voucher_no": sinv.name})
columns, data = execute(filters=filters)
self.assertEqual(len(data), 1)
self.assertEqual(expected, data[0])
filters = frappe._dict({"company": self.company, "voucher_no": sinv.name + "-1"})
columns, data = execute(filters=filters)
self.assertEqual([], data)
# date range filter
filters = frappe._dict(
{
"company": self.company,
"period_start_date": sinv.posting_date,
"period_end_date": sinv.posting_date,
}
)
columns, data = execute(filters=filters)
self.assertEqual(len(data), 1)
self.assertEqual(expected, data[0])
filters = frappe._dict(
{
"company": self.company,
"period_start_date": add_days(sinv.posting_date, -1),
"period_end_date": add_days(sinv.posting_date, -1),
}
)
columns, data = execute(filters=filters)
self.assertEqual([], data)

View File

@@ -204,7 +204,7 @@ def get_gl_entries(filters, accounting_dimensions):
)
if filters.get("presentation_currency"):
return convert_to_presentation_currency(gl_entries, currency_map, filters.get("company"))
return convert_to_presentation_currency(gl_entries, currency_map)
else:
return gl_entries

View File

@@ -12,14 +12,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
erpnext.financial_statements);
frappe.query_reports["Gross and Net Profit Report"]["filters"].push(
{
"fieldname": "project",
"label": __("Project"),
"fieldtype": "MultiSelectList",
get_data: function(txt) {
return frappe.db.get_link_options('Project', txt);
}
},
{
"fieldname": "accumulated_values",
"label": __("Accumulated Values"),

View File

@@ -15,20 +15,21 @@ from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register i
get_group_by_conditions,
get_tax_accounts,
)
from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns
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):
if not filters:
filters = {}
columns = get_columns(additional_table_columns, filters)
company_currency = erpnext.get_company_currency(filters.company)
item_list = get_items(filters, additional_query_columns)
item_list = get_items(filters, get_query_columns(additional_table_columns))
aii_account_map = get_aii_accounts()
if item_list:
itemised_tax, tax_columns = get_tax_accounts(
@@ -79,28 +80,20 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
"posting_date": d.posting_date,
"supplier": d.supplier,
"supplier_name": d.supplier_name,
**get_values_for_columns(additional_table_columns, d),
"credit_to": d.credit_to,
"mode_of_payment": d.mode_of_payment,
"project": d.project,
"company": d.company,
"purchase_order": d.purchase_order,
"purchase_receipt": purchase_receipt,
"expense_account": expense_account,
"stock_qty": d.stock_qty,
"stock_uom": d.stock_uom,
"rate": d.base_net_amount / d.stock_qty if d.stock_qty else d.base_net_amount,
"amount": d.base_net_amount,
}
if additional_query_columns:
for col in additional_query_columns:
row.update({col: d.get(col)})
row.update(
{
"credit_to": d.credit_to,
"mode_of_payment": d.mode_of_payment,
"project": d.project,
"company": d.company,
"purchase_order": d.purchase_order,
"purchase_receipt": purchase_receipt,
"expense_account": expense_account,
"stock_qty": d.stock_qty,
"stock_uom": d.stock_uom,
"rate": d.base_net_amount / d.stock_qty if d.stock_qty else d.base_net_amount,
"amount": d.base_net_amount,
}
)
total_tax = 0
for tax in tax_columns:
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
@@ -317,11 +310,6 @@ def get_conditions(filters):
def get_items(filters, additional_query_columns):
conditions = get_conditions(filters)
if additional_query_columns:
additional_query_columns = ", " + ", ".join(additional_query_columns)
else:
additional_query_columns = ""
return frappe.db.sql(
"""
select
@@ -340,11 +328,10 @@ def get_items(filters, additional_query_columns):
from `tabPurchase Invoice`, `tabPurchase Invoice Item`, `tabItem`
where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and
`tabItem`.name = `tabPurchase Invoice Item`.`item_code` and
`tabPurchase Invoice`.docstatus = 1 %s
`tabPurchase Invoice`.docstatus = 1 {1}
""".format(
additional_query_columns
)
% (conditions),
additional_query_columns, conditions
),
filters,
as_dict=1,
)

View File

@@ -9,6 +9,7 @@ from frappe.utils import cstr, flt
from frappe.utils.xlsxutils import handle_html
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns
from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import (
get_customer_details,
)
@@ -18,19 +19,14 @@ def execute(filters=None):
return _execute(filters)
def _execute(
filters=None,
additional_table_columns=None,
additional_query_columns=None,
additional_conditions=None,
):
def _execute(filters=None, additional_table_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, additional_conditions)
item_list = get_items(filters, get_query_columns(additional_table_columns), additional_conditions)
if item_list:
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency)
@@ -79,30 +75,22 @@ def _execute(
"customer": d.customer,
"customer_name": customer_record.customer_name,
"customer_group": customer_record.customer_group,
**get_values_for_columns(additional_table_columns, d),
"debit_to": d.debit_to,
"mode_of_payment": ", ".join(mode_of_payments.get(d.parent, [])),
"territory": d.territory,
"project": d.project,
"company": d.company,
"sales_order": d.sales_order,
"delivery_note": d.delivery_note,
"income_account": d.unrealized_profit_loss_account
if d.is_internal_customer == 1
else d.income_account,
"cost_center": d.cost_center,
"stock_qty": d.stock_qty,
"stock_uom": d.stock_uom,
}
if additional_query_columns:
for col in additional_query_columns:
row.update({col: d.get(col)})
row.update(
{
"debit_to": d.debit_to,
"mode_of_payment": ", ".join(mode_of_payments.get(d.parent, [])),
"territory": d.territory,
"project": d.project,
"company": d.company,
"sales_order": d.sales_order,
"delivery_note": d.delivery_note,
"income_account": d.unrealized_profit_loss_account
if d.is_internal_customer == 1
else d.income_account,
"cost_center": d.cost_center,
"stock_qty": d.stock_qty,
"stock_uom": d.stock_uom,
}
)
if d.stock_uom != d.uom and d.stock_qty:
row.update({"rate": (d.base_net_rate * d.qty) / d.stock_qty, "amount": d.base_net_amount})
else:
@@ -394,11 +382,6 @@ def get_group_by_conditions(filters, doctype):
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)
else:
additional_query_columns = ""
return frappe.db.sql(
"""
select
@@ -424,7 +407,7 @@ def get_items(filters, additional_query_columns, additional_conditions=None):
`tabItem`.name = `tabSales Invoice Item`.`item_code` and
`tabSales Invoice`.docstatus = 1 {1}
""".format(
additional_query_columns or "", conditions
additional_query_columns, conditions
),
filters,
as_dict=1,

View File

@@ -50,20 +50,20 @@ def get_pos_entries(filters, group_by_field):
order_by = "p.posting_date"
select_mop_field, from_sales_invoice_payment, group_by_mop_condition = "", "", ""
if group_by_field == "mode_of_payment":
select_mop_field = ", sip.mode_of_payment"
select_mop_field = ", sip.mode_of_payment, sip.base_amount - IF(sip.type='Cash', p.change_amount, 0) as paid_amount"
from_sales_invoice_payment = ", `tabSales Invoice Payment` sip"
group_by_mop_condition = "sip.parent = p.name AND ifnull(sip.base_amount, 0) != 0 AND"
group_by_mop_condition = "sip.parent = p.name AND ifnull(sip.base_amount - IF(sip.type='Cash', p.change_amount, 0), 0) != 0 AND"
order_by += ", sip.mode_of_payment"
elif group_by_field:
order_by += ", p.{}".format(group_by_field)
select_mop_field = ", p.base_paid_amount - p.change_amount as paid_amount "
return frappe.db.sql(
"""
SELECT
p.posting_date, p.name as pos_invoice, p.pos_profile,
p.owner, p.base_grand_total as grand_total, p.base_paid_amount - p.change_amount as paid_amount,
p.customer, p.is_return {select_mop_field}
p.owner, p.customer, p.is_return, p.base_grand_total as grand_total {select_mop_field}
FROM
`tabPOS Invoice` p {from_sales_invoice_payment}
WHERE

View File

@@ -9,14 +9,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
erpnext.utils.add_dimensions('Profit and Loss Statement', 10);
frappe.query_reports["Profit and Loss Statement"]["filters"].push(
{
"fieldname": "project",
"label": __("Project"),
"fieldtype": "MultiSelectList",
get_data: function(txt) {
return frappe.db.get_link_options('Project', txt);
}
},
{
"fieldname": "include_default_book_entries",
"label": __("Include Default Book Entries"),

View File

@@ -16,16 +16,37 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
"fieldname": "based_on",
"label": __("Based On"),
"fieldtype": "Select",
"options": ["Cost Center", "Project"],
"options": ["Cost Center", "Project", "Accounting Dimension"],
"default": "Cost Center",
"reqd": 1
"reqd": 1,
"on_change": function(query_report){
let based_on = query_report.get_values().based_on;
if(based_on!='Accounting Dimension'){
frappe.query_report.set_filter_value({
accounting_dimension: ''
});
}
}
},
{
"fieldname": "accounting_dimension",
"label": __("Accounting Dimension"),
"fieldtype": "Link",
"options": "Accounting Dimension",
"get_query": () =>{
return {
filters: {
"disabled": 0
}
}
}
},
{
"fieldname": "fiscal_year",
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
"reqd": 1,
"on_change": function(query_report) {
var fiscal_year = query_report.get_values().fiscal_year;

View File

@@ -6,6 +6,7 @@ import frappe
from frappe import _
from frappe.utils import cstr, flt
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions
from erpnext.accounts.report.financial_statements import (
filter_accounts,
filter_out_zero_value_rows,
@@ -16,10 +17,12 @@ value_fields = ("income", "expense", "gross_profit_loss")
def execute(filters=None):
if not filters.get("based_on"):
filters["based_on"] = "Cost Center"
if filters.get("based_on") == "Accounting Dimension" and not filters.get("accounting_dimension"):
frappe.throw(_("Select Accounting Dimension."))
based_on = filters.based_on.replace(" ", "_").lower()
based_on = (
filters.based_on if filters.based_on != "Accounting Dimension" else filters.accounting_dimension
)
validate_filters(filters)
accounts = get_accounts_data(based_on, filters.get("company"))
data = get_data(accounts, filters, based_on)
@@ -28,14 +31,14 @@ def execute(filters=None):
def get_accounts_data(based_on, company):
if based_on == "cost_center":
if based_on == "Cost Center":
return frappe.db.sql(
"""select name, parent_cost_center as parent_account, cost_center_name as account_name, lft, rgt
from `tabCost Center` where company=%s order by name""",
company,
as_dict=True,
)
elif based_on == "project":
elif based_on == "Project":
return frappe.get_all("Project", fields=["name"], filters={"company": company}, order_by="name")
else:
filters = {}
@@ -56,11 +59,17 @@ def get_data(accounts, filters, based_on):
gl_entries_by_account = {}
accounting_dimensions = get_dimensions(with_cost_center_and_project=True)[0]
fieldname = ""
for dimension in accounting_dimensions:
if dimension["document_type"] == based_on:
fieldname = dimension["fieldname"]
set_gl_entries_by_account(
filters.get("company"),
filters.get("from_date"),
filters.get("to_date"),
based_on,
fieldname,
gl_entries_by_account,
ignore_closing_entries=not flt(filters.get("with_period_closing_entry")),
)

View File

@@ -10,17 +10,18 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
get_dimension_with_children,
)
from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns
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):
if not filters:
filters = {}
invoice_list = get_invoices(filters, additional_query_columns)
invoice_list = get_invoices(filters, get_query_columns(additional_table_columns))
columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns(
invoice_list, additional_table_columns
)
@@ -47,13 +48,12 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
purchase_receipt = list(set(invoice_po_pr_map.get(inv.name, {}).get("purchase_receipt", [])))
project = list(set(invoice_po_pr_map.get(inv.name, {}).get("project", [])))
row = [inv.name, inv.posting_date, inv.supplier, inv.supplier_name]
if additional_query_columns:
for col in additional_query_columns:
row.append(inv.get(col))
row += [
row = [
inv.name,
inv.posting_date,
inv.supplier,
inv.supplier_name,
*get_values_for_columns(additional_table_columns, inv).values(),
supplier_details.get(inv.supplier), # supplier_group
inv.tax_id,
inv.credit_to,
@@ -244,9 +244,6 @@ def get_conditions(filters):
def get_invoices(filters, additional_query_columns):
if additional_query_columns:
additional_query_columns = ", " + ", ".join(additional_query_columns)
conditions = get_conditions(filters)
return frappe.db.sql(
"""
@@ -255,11 +252,10 @@ def get_invoices(filters, additional_query_columns):
remarks, base_net_total, base_grand_total, outstanding_amount,
mode_of_payment {0}
from `tabPurchase Invoice`
where docstatus = 1 %s
where docstatus = 1 {1}
order by posting_date desc, name desc""".format(
additional_query_columns or ""
)
% conditions,
additional_query_columns, conditions
),
filters,
as_dict=1,
)

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