Compare commits

...

1707 Commits

Author SHA1 Message Date
Frappe PR Bot
56070c7b86 chore(release): Bumped to Version 13.32.0
# [13.32.0](https://github.com/frappe/erpnext/compare/v13.31.1...v13.32.0) (2022-05-31)

### Bug Fixes

* add list view settings for Gratuity ([e4426ad](e4426addeb))
* amount precision for Tax Exemption Proof Submission ([bb04e4b](bb04e4b6de))
* component pay calculation ([b1e119d](b1e119d97d))
* conflicts ([cdb884b](cdb884bbf2))
* conflicts ([c02a89d](c02a89db4d))
* conflicts ([6aa6114](6aa6114757))
* date filter on quality inspection report (backport [#31148](https://github.com/frappe/erpnext/issues/31148)) ([#31164](https://github.com/frappe/erpnext/issues/31164)) ([f19747c](f19747cee0))
* Exchange rate reste to 1 on making mapped doc ([91863c7](91863c7457))
* Gratuity status not updated on salary slip submission ([0a014ed](0a014edd7a))
* HRA Exemption calculation in case of multiple salary structure assignments ([de8f5f8](de8f5f87d0))
* Improve button labels in Warehouse (backport [#31101](https://github.com/frappe/erpnext/issues/31101)) ([#31150](https://github.com/frappe/erpnext/issues/31150)) ([d891394](d891394dc8))
* **india:** duplicate qrcode and hide button ([#31154](https://github.com/frappe/erpnext/issues/31154)) ([f40100d](f40100d483))
* Job Opening linked to Staffing Plan cannot be created/updated if there are existing employees ([b4a3a53](b4a3a533c8))
* **pos:** freeze screen while processing pos invoices ([#30850](https://github.com/frappe/erpnext/issues/30850)) ([a7bf236](a7bf236c28))
* skip existing batch number during autogen (backport [#31140](https://github.com/frappe/erpnext/issues/31140)) ([#31144](https://github.com/frappe/erpnext/issues/31144)) ([a380bae](a380bae298))
* Tax Declaration tests and amount precision ([1e728df](1e728df54c))
* validation message ([096ae21](096ae210e1))

### Features

* Auto accrue loan interest for backdated term loans ([89c4d51](89c4d518e1))
* live preview of naming series on naming series tool (backport [#31141](https://github.com/frappe/erpnext/issues/31141)) ([#31146](https://github.com/frappe/erpnext/issues/31146)) ([fd2f3b9](fd2f3b9061))
* provision to exclude exploded items in the BOM (backport [#29450](https://github.com/frappe/erpnext/issues/29450)) ([#31174](https://github.com/frappe/erpnext/issues/31174)) ([50d338d](50d338df30))
2022-05-31 13:22:25 +00:00
Ankush Menat
683256eccc Merge pull request #31197 from frappe/version-13-hotfix
chore: weekly release for version-13
2022-05-31 18:50:44 +05:30
Rucha Mahabal
cdb884bbf2 fix: conflicts 2022-05-31 13:11:32 +05:30
Rucha Mahabal
2c26ab599a test: HRA Exemption in Proof Submission
(cherry picked from commit ed1ba677d6)
2022-05-31 13:11:32 +05:30
Rucha Mahabal
bb04e4b6de fix: amount precision for Tax Exemption Proof Submission
(cherry picked from commit cfe2f8cac1)
2022-05-31 13:11:32 +05:30
Rucha Mahabal
66deee1582 test: set country to India before running regional tests
(cherry picked from commit 2e98e9e0b9)
2022-05-31 13:11:32 +05:30
Rucha Mahabal
1e728df54c fix: Tax Declaration tests and amount precision
(cherry picked from commit 00adda7c8d)
2022-05-31 13:11:32 +05:30
Rucha Mahabal
c1f6f11e91 test: HRA Exemption in Employee Tax Exemption Declaration
(cherry picked from commit 5e96a46c87)
2022-05-31 13:11:32 +05:30
Rucha Mahabal
b1e119d97d fix: component pay calculation
(cherry picked from commit 2b65c9616f)
2022-05-31 13:11:32 +05:30
Rucha Mahabal
de8f5f87d0 fix: HRA Exemption calculation in case of multiple salary structure assignments
(cherry picked from commit 34925a3a8c)

# Conflicts:
#	erpnext/regional/india/utils.py
2022-05-31 13:11:32 +05:30
Deepesh Garg
99917af73f Merge pull request #31156 from frappe/mergify/bp/version-13-hotfix/pr-30850
fix(pos): freeze screen while processing pos invoices (backport #30850)
2022-05-31 10:56:32 +05:30
Deepesh Garg
698c9ed3bc Merge pull request #31186 from frappe/mergify/bp/version-13-hotfix/pr-31127
feat: Auto accrue loan interest for backdated term loans (backport #31127)
2022-05-31 10:28:38 +05:30
Deepesh Garg
3344823b7d Merge pull request #31185 from frappe/mergify/bp/version-13-hotfix/pr-31184
fix(accounts): Ignore Cancelled GL Entries (backport #31184)
2022-05-31 10:28:27 +05:30
Deepesh Garg
89c4d518e1 feat: Auto accrue loan interest for backdated term loans
(cherry picked from commit 96d8b1ef3c)
2022-05-31 04:28:29 +00:00
Mitchy25
d8531f20a0 Ignore Cancelled GL Entries
Profitability Analysis includes 'is_cancelled' GL Entries which means that the profit numbers are incorrect. This change will ensure that the profit figures ignore cancelled GL Entries.

(cherry picked from commit a0c412a0dd)
2022-05-31 04:19:35 +00:00
Rucha Mahabal
2fe54e5435 chore: add Interview doctypes to HR workspace (#31181) 2022-05-30 18:26:20 +05:30
mergify[bot]
0759a8aee3 chore: remove unused bill no & date from purchase receipt (backport #31163) (#31177)
chore: remove unused bill no & date from purchase receipt (#31163)

(cherry picked from commit 08bf0baaae)

Co-authored-by: Saqib Ansari <nextchamp.saqib@gmail.com>
2022-05-30 15:50:04 +05:30
mergify[bot]
50d338df30 feat: provision to exclude exploded items in the BOM (backport #29450) (#31174)
* feat: provision to exclude exploded items in the BOM (#29450)

(cherry picked from commit b75b00fefc)

* fix(ux): "New Version" button BOM

"duplicate" technically creates a new version but that's not intuitive
at all.

* fix: only erase BOM when do_not_explode is set

* fix: allow non-explosive recrusive BOMs

Recursion should be allowed as long as child item is not "exploded"
further by a BOM.

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-05-30 15:43:49 +05:30
Ankush Menat
eb8d30fe06 chore: remove framework tests from erpnext
Similar tests exist in FW and this is failing because someone updated
the translations
2022-05-30 15:42:34 +05:30
mergify[bot]
7767dc1ee3 chore: update translation fr for BOM (backport #31126) (#31165)
* chore: update translation fr for BOM (#31126)

* fix: update translation

* fix: fr translation for BOM

(cherry picked from commit ce8e05146e)

* chore: format

Co-authored-by: HENRY Florian <florian.henry@open-concept.pro>
Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-05-29 22:10:46 +05:30
mergify[bot]
f19747cee0 fix: date filter on quality inspection report (backport #31148) (#31164)
fix: date filter on quality inspection report (#31148)

* fix: date filter

fix from date to to date filter btw those days

* fix: remove unnecessary conditions

Co-authored-by: Ankush Menat <ankushmenat@gmail.com>
(cherry picked from commit 7ff8acac51)

Co-authored-by: MOHAMMED NIYAS <76736615+niyazrazak@users.noreply.github.com>
2022-05-27 17:32:24 +05:30
Rucha Mahabal
b98f3647ec Merge pull request #31162 from frappe/mergify/bp/version-13-hotfix/pr-31160
fix: Gratuity status not updated on salary slip submission (backport #31160)
2022-05-27 15:49:08 +05:30
Rucha Mahabal
c02a89db4d fix: conflicts 2022-05-27 15:30:29 +05:30
Rucha Mahabal
6aa6114757 fix: conflicts 2022-05-27 15:11:49 +05:30
Rucha Mahabal
30cbe8feb6 test: make holiday list before running gratuity tests
(cherry picked from commit c9e070393d)
2022-05-27 09:36:21 +00:00
Rucha Mahabal
e4426addeb fix: add list view settings for Gratuity
(cherry picked from commit 79b0aede00)

# Conflicts:
#	erpnext/payroll/doctype/gratuity/gratuity.json
2022-05-27 09:36:21 +00:00
Rucha Mahabal
b5d66877d8 refactor: clean-up gratuity tests
(cherry picked from commit 6c66bbbbfe)

# Conflicts:
#	erpnext/payroll/doctype/gratuity/test_gratuity.py
2022-05-27 09:36:20 +00:00
Rucha Mahabal
00a6bc7970 test: Gratuity status for payment via salary slip
(cherry picked from commit b81d7519c1)

# Conflicts:
#	erpnext/payroll/doctype/gratuity/test_gratuity.py
2022-05-27 09:36:20 +00:00
Rucha Mahabal
0a014edd7a fix: Gratuity status not updated on salary slip submission
(cherry picked from commit 385e22a067)
2022-05-27 09:36:19 +00:00
Frappe PR Bot
1bffd06e3a chore(release): Bumped to Version 13.31.1
## [13.31.1](https://github.com/frappe/erpnext/compare/v13.31.0...v13.31.1) (2022-05-27)

### Bug Fixes

* Exchange rate reste to 1 on making mapped doc ([e5b586f](e5b586ffd5))
2022-05-27 06:50:43 +00:00
Deepesh Garg
0371c62eae Merge pull request #31158 from frappe/mergify/bp/version-13/pr-31155
fix: Exchange rate reste to 1 on making mapped doc (backport #31155)
2022-05-27 12:19:09 +05:30
Deepesh Garg
f820d8d35e Merge pull request #31157 from frappe/mergify/bp/version-13-hotfix/pr-31155
fix: Exchange rate reste to 1 on making mapped doc (backport #31155)
2022-05-27 12:18:26 +05:30
Deepesh Garg
e5b586ffd5 fix: Exchange rate reste to 1 on making mapped doc
(cherry picked from commit 2a10f09d8d)
2022-05-27 06:47:55 +00:00
Deepesh Garg
91863c7457 fix: Exchange rate reste to 1 on making mapped doc
(cherry picked from commit 2a10f09d8d)
2022-05-27 06:47:35 +00:00
mergify[bot]
f40100d483 fix(india): duplicate qrcode and hide button (#31154) 2022-05-27 12:16:47 +05:30
HarryPaulo
a7bf236c28 fix(pos): freeze screen while processing pos invoices (#30850)
(cherry picked from commit 4b04694c2c)
2022-05-27 06:46:26 +00:00
mergify[bot]
d891394dc8 fix: Improve button labels in Warehouse (backport #31101) (#31150)
* style: format warehouse js

(cherry picked from commit c704ad889d)

* fix: improve labels, simplify logic

(cherry picked from commit a6ddd86d31)

* fix: german translations

(cherry picked from commit 9356eb11de)

* fix: remove unsupported arguments

Co-authored-by: Ankush Menat <ankushmenat@gmail.com>
(cherry picked from commit e77c379cbb)

* refactor: set queries during setup

(cherry picked from commit 1b16eb7667)

* style: format

(cherry picked from commit 1e9f9c452f)

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
2022-05-27 12:11:12 +05:30
mergify[bot]
a380bae298 fix: skip existing batch number during autogen (backport #31140) (#31144)
* fix: skip existing batch number during autogen (#31140)

* test: correctly check for existing item

* test: batch no for test PR generation

Co-authored-by: Ankush Menat <me@ankush.dev>
Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-05-26 20:55:29 +05:30
mergify[bot]
fd2f3b9061 feat: live preview of naming series on naming series tool (backport #31141) (#31146)
* chore: resave naming series doctype schema

separate commit to avoid mixing actual changes

(cherry picked from commit 82cd54b40b)

* feat: preview next numbers on naming series tool

(cherry picked from commit 24d1bf5328)

* docs: update help information on naming series

(cherry picked from commit 4d0e2aa33a)

* test: add basic tests for naming series tool

(cherry picked from commit 964b4184a6)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-05-26 17:10:49 +05:30
Rucha Mahabal
2ce90afa25 Merge pull request #31142 from frappe/mergify/bp/version-13-hotfix/pr-31130 2022-05-26 13:42:48 +05:30
Rucha Mahabal
096ae210e1 fix: validation message
(cherry picked from commit 2bc6d46070)
2022-05-26 07:54:35 +00:00
Rucha Mahabal
ec63912253 test: Job Opening against a Staffing Plan
(cherry picked from commit ab0ef60918)
2022-05-26 07:54:35 +00:00
Rucha Mahabal
b4a3a533c8 fix: Job Opening linked to Staffing Plan cannot be created/updated if there are existing employees
(cherry picked from commit 29228575fa)
2022-05-26 07:54:34 +00:00
Frappe PR Bot
91e935fee7 chore(release): Bumped to Version 13.31.0
# [13.31.0](https://github.com/frappe/erpnext/compare/v13.30.0...v13.31.0) (2022-05-26)

### Bug Fixes

* Account currency validation for first transaction ([228f10b](228f10bf30))
* Add party account validation for journal entry ([7f853b1](7f853b1f0f))
* always update item_name for stock entry (backport [#31068](https://github.com/frappe/erpnext/issues/31068)) ([#31071](https://github.com/frappe/erpnext/issues/31071)) ([f519dc6](f519dc613c))
* change project's actual_start_date fieldtype from Data to Date (backport [#31085](https://github.com/frappe/erpnext/issues/31085)) ([#31135](https://github.com/frappe/erpnext/issues/31135)) ([8c2f203](8c2f203361))
* Chart data for monthly periodicity in Cash Flow report ([#31039](https://github.com/frappe/erpnext/issues/31039)) ([f14e9b7](f14e9b7502))
* corrective job card creation (backport [#31083](https://github.com/frappe/erpnext/issues/31083)) ([#31084](https://github.com/frappe/erpnext/issues/31084)) ([c17c260](c17c260a65))
* don't capture payments with invoice_id as donations ([168a9d4](168a9d417b))
* employee advance status update on return via additional salary ([d59c3d2](d59c3d2142))
* Handle missing HSN Codes ([ce3a21e](ce3a21eb03))
* Healthcare module accounting test cases ([09a42a1](09a42a122f))
* **India:** Async issue in company address trigger ([2ea3318](2ea331852a))
* **india:** error while parsing e-invoice ([#31061](https://github.com/frappe/erpnext/issues/31061)) ([1461d66](1461d66dda))
* **india:** eway bill cancel api is disabled ([#31060](https://github.com/frappe/erpnext/issues/31060)) ([95491e1](95491e1718))
* Job Card excess transfer behaviour (backport [#31054](https://github.com/frappe/erpnext/issues/31054)) ([#31096](https://github.com/frappe/erpnext/issues/31096)) ([3984f04](3984f04a49))
* Leave Encashment calculations (backport [#31062](https://github.com/frappe/erpnext/issues/31062)) ([#31091](https://github.com/frappe/erpnext/issues/31091)) ([ba76b64](ba76b6419e))
* Loan Doc query in Bank Reconciliation Statement ([611d1af](611d1af526))
* Loan repayment entries for payroll payable account ([ea6d754](ea6d754f73))
* multiple entries for same payment term ([90b1147](90b1147365))
* Party account validation in JV ([d10c2e5](d10c2e50be))
* payments duplicate on pos closing entry (backport [#30976](https://github.com/frappe/erpnext/issues/30976)) ([#31005](https://github.com/frappe/erpnext/issues/31005)) ([0efbabe](0efbabe7cf))
* **pos:** paid amount calculation for multicurrency invoice ([#31122](https://github.com/frappe/erpnext/issues/31122)) ([98eb7da](98eb7da06a))
* remove bad default for Membership From Date ([34928d2](34928d29f1))
* Remove validation from Journal Entry ([4ca6cdc](4ca6cdca76))
* replace document states with list settings ([78e9e66](78e9e66d63))
* timesheet fetching in sales invoice ([216c32f](216c32f4bc))
* Use directly <a> and style it as button instead of using button ([0ab9fc0](0ab9fc0040))

### Features

* **Employee Advance:** add 'Returned' and 'Partly Claimed and Returned' status ([42e7a86](42e7a86a3b))

### Reverts

* Revert "fix: Add party account validation for journal entry" ([9d43a90](9d43a90eb0))
2022-05-26 05:38:49 +00:00
Deepesh Garg
63288fcd6c Merge pull request #31137 from frappe/version-13-hotfix
chore: Weekly release for version-13
2022-05-26 11:07:11 +05:30
mergify[bot]
8c2f203361 fix: change project's actual_start_date fieldtype from Data to Date (backport #31085) (#31135)
Co-authored-by: sersaber <93864988+sersaber@users.noreply.github.com>
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-05-26 10:32:10 +05:30
Deepesh Garg
0b0d57f7c2 Merge pull request #31128 from frappe/mergify/bp/version-13-hotfix/pr-26916
fix: Account currency validation for first transaction (backport #26916)
2022-05-26 09:41:19 +05:30
Deepesh Garg
5748af7d53 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-26916 2022-05-26 09:23:46 +05:30
Deepesh Garg
7df829f9cc chore: Update test case 2022-05-26 09:03:18 +05:30
Deepesh Garg
3d51d125bf chore: Update test case 2022-05-25 23:52:40 +05:30
Deepesh Garg
68d1787eeb Merge pull request #31124 from nextchamp-saqib/fix-timesheet-fetch
fix: timesheet fetching in sales invoice
2022-05-25 20:15:19 +05:30
Deepesh Garg
a61d7558eb Merge pull request #31132 from frappe/mergify/bp/version-13-hotfix/pr-31039
fix: Chart data for monthly periodicity in Cash Flow report (backport #31039)
2022-05-25 20:10:08 +05:30
Deepesh Garg
14422eaf59 chore: Update test cases 2022-05-25 20:06:31 +05:30
Deepesh Garg
4aed7eda91 Merge pull request #31131 from frappe/mergify/bp/version-13-hotfix/pr-31129
fix: Loan Doc query in Bank Reconciliation Statement (backport #31129)
2022-05-25 19:41:40 +05:30
xdlumertz
f14e9b7502 fix: Chart data for monthly periodicity in Cash Flow report (#31039)
fix: Chart data for monthly periodicity in Cash Flow report
(cherry picked from commit c5e922c76b)
2022-05-25 14:10:33 +00:00
Deepesh Garg
cd00cb2fb7 chore: Linting Issues
(cherry picked from commit 9e4a36089e)
2022-05-25 13:52:10 +00:00
Deepesh Garg
74ddf261e9 chore: Linting Issues
(cherry picked from commit a1f53f8d31)
2022-05-25 13:52:09 +00:00
Deepesh Garg
611d1af526 fix: Loan Doc query in Bank Reconciliation Statement
(cherry picked from commit 147fc8fde7)
2022-05-25 13:52:08 +00:00
Deepesh Garg
3fab8a2213 chore: Remove unintended changes 2022-05-25 19:20:09 +05:30
Deepesh Garg
f724f6d1bb chore: Resolve conflicts 2022-05-25 19:04:20 +05:30
Deepesh Garg
a5bd76bbb1 Merge pull request #31076 from frappe/mergify/bp/version-13-hotfix/pr-31004
fix: duplicate entries in payment terms report output (backport #31004)
2022-05-25 17:09:15 +05:30
Deepesh Garg
761669c7ca chore: Update test case
(cherry picked from commit bc34737709)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
2022-05-25 11:33:46 +00:00
Deepesh Garg
0628785c64 test: Update test cases
(cherry picked from commit 65232edfd5)

# Conflicts:
#	erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
#	erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
2022-05-25 11:33:45 +00:00
Deepesh Garg
4ca6cdca76 fix: Remove validation from Journal Entry
(cherry picked from commit 5b8726405d)

# Conflicts:
#	erpnext/accounts/doctype/journal_entry/journal_entry.py
2022-05-25 11:33:44 +00:00
Deepesh Garg
d10c2e50be fix: Party account validation in JV
(cherry picked from commit 417d6abcf4)

# Conflicts:
#	erpnext/accounts/doctype/journal_entry/journal_entry.py
2022-05-25 11:33:43 +00:00
Deepesh Garg
4727482737 test: Set default currency for patient
(cherry picked from commit 30876a105c)

# Conflicts:
#	erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
2022-05-25 11:33:43 +00:00
Deepesh Garg
e28f6b7d31 test: fix property name
(cherry picked from commit c10a22529c)

# Conflicts:
#	erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
2022-05-25 11:33:42 +00:00
Deepesh Garg
8f969fbd66 test: Update test cases for currency change validation
(cherry picked from commit 60915e874d)

# Conflicts:
#	erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
#	erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
#	erpnext/healthcare/doctype/therapy_plan/therapy_plan.py
#	erpnext/non_profit/doctype/membership/membership.py
2022-05-25 11:33:42 +00:00
Deepesh Garg
9d43a90eb0 Revert "fix: Add party account validation for journal entry"
This reverts commit f00620a3ca.

(cherry picked from commit 0a618817dc)
2022-05-25 11:33:39 +00:00
Deepesh Garg
09a42a122f fix: Healthcare module accounting test cases
(cherry picked from commit bcaf475295)

# Conflicts:
#	erpnext/healthcare/doctype/lab_test/test_lab_test.py
2022-05-25 11:33:39 +00:00
Deepesh Garg
7f853b1f0f fix: Add party account validation for journal entry
(cherry picked from commit f00620a3ca)
2022-05-25 11:33:38 +00:00
Deepesh Garg
228f10bf30 fix: Account currency validation for first transaction
(cherry picked from commit 80c85dd17c)

# Conflicts:
#	erpnext/controllers/accounts_controller.py
2022-05-25 11:33:38 +00:00
Deepesh Garg
3528d6fbdb Merge pull request #31121 from frappe/mergify/bp/version-13-hotfix/pr-31077
fix: Loan repayment entries for payroll payable account (backport #31077)
2022-05-25 12:37:17 +05:30
Deepesh Garg
2ae085d258 Merge pull request #31123 from deepeshgarg007/hsn_wise_tax_rate_none_type
fix: Handle missing HSN Codes
2022-05-25 12:34:26 +05:30
Deepesh Garg
187768892d Merge pull request #31120 from frappe/mergify/bp/version-13-hotfix/pr-31080
fix(India): Async issue in company address trigger (backport #31080)
2022-05-25 12:33:37 +05:30
mergify[bot]
98eb7da06a fix(pos): paid amount calculation for multicurrency invoice (#31122) 2022-05-25 12:24:09 +05:30
Saqib Ansari
216c32f4bc fix: timesheet fetching in sales invoice 2022-05-25 12:12:35 +05:30
Deepesh Garg
ce3a21eb03 fix: Handle missing HSN Codes 2022-05-25 11:51:07 +05:30
Deepesh Garg
ea6d754f73 fix: Loan repayment entries for payroll payable account
(cherry picked from commit 3128f9603e)
2022-05-25 05:54:08 +00:00
Deepesh Garg
559fc509e7 chore: Linting issues
(cherry picked from commit 8fd0b3b9f5)
2022-05-25 05:53:45 +00:00
Deepesh Garg
2ea331852a fix(India): Async issue in company address trigger
(cherry picked from commit c41f9f046f)
2022-05-25 05:53:45 +00:00
Rucha Mahabal
22aeacb088 Merge pull request #31104 from ruchamahabal/emp-adv-status-v13
feat(Employee Advance): add 'Returned' and 'Partly Claimed and Returned'
2022-05-25 11:09:52 +05:30
Rucha Mahabal
7ac0624f68 Merge branch 'version-13-hotfix' into emp-adv-status-v13 2022-05-25 10:46:25 +05:30
Rucha Mahabal
a071095886 Merge pull request #31110 from ruchamahabal/fix-npo-membership 2022-05-24 18:38:43 +05:30
Rucha Mahabal
168a9d417b fix: don't capture payments with invoice_id as donations
- if donations and subscriptions are set up in the same dashboard, membership payments also trigger payment webhook

- in order to differentiate there is already a check for RP's auto generated description but if subscriptions are configured using subscription links, RP doesn't send descriptions

- use invoice_id to ignore such payments instead
2022-05-24 18:16:56 +05:30
Rucha Mahabal
34928d29f1 fix: remove bad default for Membership From Date 2022-05-24 18:14:06 +05:30
Ankush Menat
e9968cc6fc chore: disable feed for material request 2022-05-24 14:33:57 +05:30
Rucha Mahabal
ab9744fe97 Merge branch 'version-13-hotfix' into emp-adv-status-v13 2022-05-24 11:49:24 +05:30
Rucha Mahabal
78e9e66d63 fix: replace document states with list settings 2022-05-24 11:29:58 +05:30
Rucha Mahabal
b265ca467c test: test advance filters in expense claim and cancelled status 2022-05-24 11:19:14 +05:30
Rucha Mahabal
806752111e test: employee advance status 2022-05-24 11:13:18 +05:30
Rucha Mahabal
d59c3d2142 fix: employee advance status update on return via additional salary 2022-05-24 11:11:44 +05:30
Rucha Mahabal
cac9e245b6 patch: Employee Advance return statuses 2022-05-24 11:11:10 +05:30
Rucha Mahabal
42e7a86a3b feat(Employee Advance): add 'Returned' and 'Partly Claimed and Returned' status 2022-05-24 11:09:42 +05:30
mergify[bot]
c2a08f1285 chore: error logging for auto material requests (backport #31103) (#31105)
chore: error logging for auto material requests (#31103)

(cherry picked from commit ecb39d81e0)

Co-authored-by: Ankush Menat <me@ankush.dev>
2022-05-24 10:41:50 +05:30
mergify[bot]
3984f04a49 fix: Job Card excess transfer behaviour (backport #31054) (#31096)
* fix: Job Card excess transfer behaviour

- Block excess transfer of items if not allowed in settings
- Behaviour made consistent with js behaviour (button disappears if not pending and not allowed in settings)
- Test for same case

(cherry picked from commit e07ce6efe0)

# Conflicts:
#	erpnext/manufacturing/doctype/job_card/test_job_card.py

* chore: Run `_validate_over_transfer` only if excess transfer is blocked in settings

(cherry picked from commit 9f6e10663b)

* chore: conflicts

* chore: missing conflict resolution changes

Co-authored-by: marination <maricadsouza221197@gmail.com>
Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-05-23 15:20:47 +05:30
mergify[bot]
f6b2f36ca8 test: search test failing because of stale data (backport #31098) (#31099)
test: search test failing because of stale data (#31098)

(cherry picked from commit a36174afdf)

Co-authored-by: Ankush Menat <me@ankush.dev>
2022-05-23 15:13:13 +05:30
Suraj Shetty
1c3ad13f6c Merge pull request #31094 from frappe/mergify/bp/version-13-hotfix/pr-31092 2022-05-23 12:20:53 +05:30
mergify[bot]
ddee0893e6 fix translation German "Designation" (backport #31082) (#31093)
fix translation German "Designation" (#31082)

changed "Bezeichnung" to "Position" as the is more precice in the field of employment which erpnext refers to here

(cherry picked from commit 348a674df9)

Co-authored-by: Wolfram Schmidt <wolfram.schmidt@phamos.eu>
2022-05-23 12:02:30 +05:30
Suraj Shetty
0ab9fc0040 fix: Use directly <a> and style it as button instead of using button
Since few email servers (like outlook) strips out link in the button making them unclickable.

(cherry picked from commit a29b92febc)
2022-05-23 06:27:37 +00:00
mergify[bot]
ba76b6419e fix: Leave Encashment calculations (backport #31062) (#31091)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-05-23 10:32:41 +05:30
mergify[bot]
c17c260a65 fix: corrective job card creation (backport #31083) (#31084)
* test: simplify job card tests

(cherry picked from commit e625394488)

* fix: creation of corrective job card fails

This used to fail because sub_operations is a child table that's not
initalized by default till v13, in develop branch we init tables with
empty list.

(cherry picked from commit 66cf9aa344)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-05-21 14:28:27 +05:30
ruthra kumar
90b1147365 fix: multiple entries for same payment term
(cherry picked from commit e826093150)
2022-05-20 02:24:23 +00:00
mergify[bot]
f519dc613c fix: always update item_name for stock entry (backport #31068) (#31071)
fix: always update item_name for stock entry (#31068)

If item_name is already set and for some reason becomes outdated then
it's not updated in backend.

Fix: always set item_name and stock_uom when fetching item details
(cherry picked from commit 6d6616dbcd)

Co-authored-by: Ankush Menat <me@ankush.dev>
2022-05-19 16:02:20 +05:30
mergify[bot]
0efbabe7cf fix: payments duplicate on pos closing entry (backport #30976) (#31005) 2022-05-19 14:27:19 +05:30
mergify[bot]
1461d66dda fix(india): error while parsing e-invoice (#31061) 2022-05-18 12:44:24 +05:30
mergify[bot]
95491e1718 fix(india): eway bill cancel api is disabled (#31060) 2022-05-18 12:44:07 +05:30
Frappe PR Bot
d9f76478da chore(release): Bumped to Version 13.30.0
# [13.30.0](https://github.com/frappe/erpnext/compare/v13.29.2...v13.30.0) (2022-05-17)

### Bug Fixes

* `set_missing_values` in SE and re-use the same on all SE mappings ([fe52c1f](fe52c1f354))
* Add validation for SEZ and Export invoices without payment of taxes ([cb8453d](cb8453dac8))
* allow to use formatting for the field to_discuss in opportunity ([e126d4e](e126d4e592))
* Block 0 Qty via Update Items to be consistent with form validation ([5647875](56478752e4))
* Calculate totals even though pricing rule is not applied on mapped doc ([678a01d](678a01d4bd))
* **charts:** Pass fieldtype for chart data in selling reports ([917e7c3](917e7c34af))
* conflicts ([87fd933](87fd93370a))
* conflicts ([fb62bbf](fb62bbf61a))
* disable pricing rules for internal transfers (backport [#31034](https://github.com/frappe/erpnext/issues/31034)) ([#31036](https://github.com/frappe/erpnext/issues/31036)) ([d5eb9fb](d5eb9fb3fd))
* discount ledger entry in case of multicurrency invoice ([#31047](https://github.com/frappe/erpnext/issues/31047)) ([c3417e4](c3417e4103))
* dont fail repost for recoverable errors (backport [#30979](https://github.com/frappe/erpnext/issues/30979)) ([#31023](https://github.com/frappe/erpnext/issues/31023)) ([a019cb6](a019cb6caa))
* **Employee Advance:** Return/Deduction from Salary button visibility (backport [#31011](https://github.com/frappe/erpnext/issues/31011)) ([#31012](https://github.com/frappe/erpnext/issues/31012)) ([5b1d85e](5b1d85e8bf))
* Failing accounting dimension patch ([b14a7b8](b14a7b8a5d))
* german translations for Employee ([b9bda04](b9bda04a83))
* gl entry validation for miniscule loan penalty ([e958ef2](e958ef26e0))
* hide template items from sales/purchase order ([8b99f43](8b99f43c61))
* IN time not captured in Attendance through Employee Checkin (backport [#31029](https://github.com/frappe/erpnext/issues/31029)) ([#31031](https://github.com/frappe/erpnext/issues/31031)) ([477bbcc](477bbcc9e5))
* Item rate reset on changing posting date ([#30990](https://github.com/frappe/erpnext/issues/30990)) ([8ef649f](8ef649f65d))
* Just add one rate in GST HSN Code ([ed76687](ed76687dac))
* Merge Conflicts ([3abf264](3abf26428c))
* Multiple fixes in GSTR-1 report ([f2cbb70](f2cbb70325))
* **patch:** avoid checking for return field if it doesnt exits (backport [#30995](https://github.com/frappe/erpnext/issues/30995)) ([#30997](https://github.com/frappe/erpnext/issues/30997)) ([a94b5c0](a94b5c0d8b))
* per_billed for return DN (backport [#30868](https://github.com/frappe/erpnext/issues/30868)) ([#30971](https://github.com/frappe/erpnext/issues/30971)) ([97ea1f5](97ea1f5123))
* precision loss when transferring  (backport [#30834](https://github.com/frappe/erpnext/issues/30834)) ([#31032](https://github.com/frappe/erpnext/issues/31032)) ([fc80a50](fc80a50640))
* precision of total penalty paid ([ad21853](ad21853b01))
* precision of total penalty paid ([5c45737](5c45737a8f))
* prevent bypassing forced valuation rate (backport [#30987](https://github.com/frappe/erpnext/issues/30987)) ([#31020](https://github.com/frappe/erpnext/issues/31020)) ([706c19d](706c19db70))
* pro rata calculation for monthly depreciation ([#30989](https://github.com/frappe/erpnext/issues/30989)) ([408d952](408d952332))
* remove item attribute limit from variant selector (backport [#31026](https://github.com/frappe/erpnext/issues/31026)) ([#31028](https://github.com/frappe/erpnext/issues/31028)) ([1f016e9](1f016e9137))
* Set actual qty and basic rate in SE on warehouse triggers (`get_warehouse_details`) ([30b0aee](30b0aee013))
* stock analytics report shows incorrect data there's no stock movement in a period (backport [#30945](https://github.com/frappe/erpnext/issues/30945)) ([#30980](https://github.com/frappe/erpnext/issues/30980)) ([295ffb3](295ffb3f1a))
* translation for status filter ([e5f8231](e5f8231632))
* **translations:** Update ru translations ([#30992](https://github.com/frappe/erpnext/issues/30992)) ([f797005](f797005384))
* TypeError in add_indicator_for_multicompany (backport [#31042](https://github.com/frappe/erpnext/issues/31042)) ([#31048](https://github.com/frappe/erpnext/issues/31048)) ([e24bb1d](e24bb1dbf1))
* unlink Attendance from Employee Checkins on cancellation (backport [#31045](https://github.com/frappe/erpnext/issues/31045)) ([#31049](https://github.com/frappe/erpnext/issues/31049)) ([e03fe97](e03fe97a6e))
* UOM in HSN-wise summary of outward supply ([cd7d5cd](cd7d5cdb22))
* user can select disabled accounts in taxes table ([047c879](047c879bec))
* validate disabled accounts before posting ledger entries ([515e49b](515e49bb90))
* validate on hold purchase invoices in payment entry ([9fbd170](9fbd170fa4))

### Features

* add Employee Status filter in leave balance reports ([716b525](716b5253a4))
* add Link to Opportunity ([#30614](https://github.com/frappe/erpnext/issues/30614)) ([bc23bc7](bc23bc738e))
* request_for_quotation ([db4e264](db4e264d92))
* request_for_quotation - refactor ([b6a3e69](b6a3e693ae))
* select multiple values for accounting dimension (backport [#31015](https://github.com/frappe/erpnext/issues/31015)) ([#31041](https://github.com/frappe/erpnext/issues/31041)) ([9c21eb5](9c21eb5b94))
2022-05-17 07:41:35 +00:00
Ankush Menat
3f8816f2ce Merge pull request #31050 from ankush/v13_release
chore: release
2022-05-17 13:09:46 +05:30
mergify[bot]
e03fe97a6e fix: unlink Attendance from Employee Checkins on cancellation (backport #31045) (#31049)
* fix: unlink Attendance from Employee Checkins on cancellation (#31045)

(cherry picked from commit 28fe4f3d54)

* fix: import missing function

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-05-17 12:35:27 +05:30
mergify[bot]
c3417e4103 fix: discount ledger entry in case of multicurrency invoice (#31047) 2022-05-17 12:22:08 +05:30
mergify[bot]
9c21eb5b94 feat: select multiple values for accounting dimension (backport #31015) (#31041)
fix: select multiple values for accounting dimenssion

(cherry picked from commit 69be22ba7c)

Co-authored-by: Rohit Waghchaure <rohitw1991@gmail.com>
2022-05-17 11:56:31 +05:30
mergify[bot]
e24bb1dbf1 fix: TypeError in add_indicator_for_multicompany (backport #31042) (#31048)
fix: TypeError in add_indicator_for_multicompany (#31042)

Minor fix in add_indicator_for_multicompany

In case of multi-company transactions add (+) buttons in connection dashboard pf customer aren't being loaded due to TypeError (TypeError: e.dashboard.stats_area.removeClass is not a function) created by "frm.dashboard.stats_area.removeClass('hidden');" during the stats section creation.

(cherry picked from commit 867f2c6282)

Co-authored-by: Deepak <36790711+dpk404@users.noreply.github.com>
2022-05-17 11:54:50 +05:30
Ganga Manoj
408d952332 fix: pro rata calculation for monthly depreciation (#30989) 2022-05-17 11:21:10 +05:30
mergify[bot]
d5eb9fb3fd fix: disable pricing rules for internal transfers (backport #31034) (#31036)
fix: disable pricing rules for internal transfers (#31034)

* fix: disable pricing rules for internal transfers

* fix: only apply validation on internal transfers

* fix: internal_party_field undefined

(cherry picked from commit 3714e36b44)

Co-authored-by: Ankush Menat <me@ankush.dev>
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2022-05-16 18:34:39 +05:30
Marica
d7bea97095 Merge pull request #31030 from frappe/mergify/bp/version-13-hotfix/pr-30992
fix(translations): Update ru translations (backport #30992)
2022-05-16 17:35:17 +05:30
Marica
3abf26428c fix: Merge Conflicts 2022-05-16 17:13:00 +05:30
mergify[bot]
477bbcc9e5 fix: IN time not captured in Attendance through Employee Checkin (backport #31029) (#31031)
* fix: IN time not captured in Attendance through Employee Checkin (#31029)

(cherry picked from commit 1b7ce9649b)

# Conflicts:
#	erpnext/hr/doctype/employee_checkin/test_employee_checkin.py

* fix: conflicts

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-05-16 17:05:26 +05:30
mergify[bot]
fc80a50640 fix: precision loss when transferring (backport #30834) (#31032)
* fix: stock transfer value when precision differs

(cherry picked from commit b1c90e9949)

# Conflicts:
#	erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py

* fix: Merge conflicts

* chore: Remove unused `flt` (sider)

Co-authored-by: Ankush Menat <ankush@frappe.io>
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2022-05-16 17:02:47 +05:30
Vladislav
f797005384 fix(translations): Update ru translations (#30992)
* fix incorrect translation

* Update ru.csv

* Update ru.csv

* Update erpnext/translations/ru.csv

Co-authored-by: Marica <maricadsouza221197@gmail.com>

Co-authored-by: Marica <maricadsouza221197@gmail.com>
(cherry picked from commit af1eab5d54)

# Conflicts:
#	erpnext/translations/ru.csv
2022-05-16 09:49:23 +00:00
mergify[bot]
1f016e9137 fix: remove item attribute limit from variant selector (backport #31026) (#31028)
fix: remove item attribute limit from variant selector

(cherry picked from commit 6bd1cb9235)

Co-authored-by: Ankush Menat <me@ankush.dev>
2022-05-16 14:13:38 +05:30
Marica
a0930a5619 Merge pull request #31027 from frappe/mergify/bp/version-13-hotfix/pr-31025
fix: hide template items from sales/purchase order (backport #31025)
2022-05-16 14:11:24 +05:30
Ankush Menat
8b99f43c61 fix: hide template items from sales/purchase order
(cherry picked from commit 0e875f5049)
2022-05-16 08:31:07 +00:00
mergify[bot]
a019cb6caa fix: dont fail repost for recoverable errors (backport #30979) (#31023)
recoverable erros:
1. timeout
2. lock/deadlocks

(cherry picked from commit 80d959c579)

Co-authored-by: Ankush Menat <me@ankush.dev>
2022-05-16 12:08:32 +05:30
mergify[bot]
6adeaa1de6 test: check translation files (backport #31006) (#31007)
This is an automatic backport of pull request #31006 done by [Mergify](https://mergify.com).


---


<details>
<summary>Mergify commands and options</summary>

<br />

More conditions and actions can be found in the [documentation](https://docs.mergify.com/).

You can also trigger Mergify actions by commenting on this pull request:

- `@Mergifyio refresh` will re-evaluate the rules
- `@Mergifyio rebase` will rebase this PR on its base branch
- `@Mergifyio update` will merge the base branch into this PR
- `@Mergifyio backport <destination>` will backport this PR on `<destination>` branch

Additionally, on Mergify [dashboard](https://dashboard.mergify.com/) you can:

- look at your merge queues
- generate the Mergify configuration with the config editor.

Finally, you can contact us on https://mergify.com
</details>
2022-05-16 06:13:32 +00:00
Deepesh Garg
e2028a0d6d Merge pull request #31021 from frappe/mergify/bp/version-13-hotfix/pr-31010
fix: GL entry validation for minuscule loan penalty (backport #31010)
2022-05-15 23:38:07 +05:30
mergify[bot]
706c19db70 fix: prevent bypassing forced valuation rate (backport #30987) (#31020)
fix: prevent bypassing forced valuation rate

if you edit "margin_rate_or_amount" after saving DN then based on
selected margin the rate gets updated which isn't valuation rate.

(cherry picked from commit ee0a277540)

Co-authored-by: Ankush Menat <me@ankush.dev>
2022-05-15 21:15:24 +05:30
Abhinav Raut
ad21853b01 fix: precision of total penalty paid
(cherry picked from commit 589dd17e58)
2022-05-15 15:21:09 +00:00
Abhinav Raut
5c45737a8f fix: precision of total penalty paid
(cherry picked from commit 9a86885c0a)
2022-05-15 15:21:09 +00:00
Abhinav Raut
e958ef26e0 fix: gl entry validation for miniscule loan penalty
(cherry picked from commit aef65e7417)
2022-05-15 15:21:08 +00:00
Deepesh Garg
c7f4a1595a Merge pull request #30978 from deepeshgarg007/gstr_export_sez_tax_rates
fix: Multiple fixes in GST reporting
2022-05-15 20:33:32 +05:30
Deepesh Garg
5b02b5b3cd chore: Remove unintended changes 2022-05-15 16:42:34 +05:30
Deepesh Garg
ed76687dac fix: Just add one rate in GST HSN Code 2022-05-15 16:40:33 +05:30
Deepesh Garg
c54c01e0de Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into gstr_export_sez_tax_rates 2022-05-14 21:33:16 +05:30
Rucha Mahabal
c4650160ce Merge pull request #31014 from frappe/mergify/bp/version-13-hotfix/pr-31013 2022-05-13 22:33:48 +05:30
Rucha Mahabal
64b58ac031 test: employee status filter in leave balance reports
(cherry picked from commit 260cfa5d1e)
2022-05-13 16:37:57 +00:00
Rucha Mahabal
e5f8231632 fix: translation for status filter
(cherry picked from commit 08fb9a4318)
2022-05-13 16:37:56 +00:00
Rucha Mahabal
716b5253a4 feat: add Employee Status filter in leave balance reports
(cherry picked from commit ed8a49737a)
2022-05-13 16:37:56 +00:00
mergify[bot]
5b1d85e8bf fix(Employee Advance): Return/Deduction from Salary button visibility (backport #31011) (#31012)
fix(Employee Advance): Return/Deduction from Salary button visibility (#31011)

(cherry picked from commit 3016ed958e)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-05-13 20:06:26 +05:30
Deepesh Garg
cb8453dac8 fix: Add validation for SEZ and Export invoices without payment of taxes 2022-05-13 18:12:18 +05:30
Deepesh Garg
8af30bcc16 chore: Linting Issues 2022-05-13 18:12:18 +05:30
Deepesh Garg
cd7d5cdb22 fix: UOM in HSN-wise summary of outward supply 2022-05-13 18:12:18 +05:30
Frappe PR Bot
7369db59aa chore(release): Bumped to Version 13.29.0
# [13.29.0](https://github.com/frappe/erpnext/compare/v13.28.0...v13.29.0) (2022-05-10)

### Bug Fixes

* Consider paryt and party type as well in group by consolidated view ([189fc89](189fc89e2d))
* disable form save on naming series tool ([#30909](https://github.com/frappe/erpnext/issues/30909)) ([#30910](https://github.com/frappe/erpnext/issues/30910)) ([d60a6cb](d60a6cb2f8))
* double future qty updates ([0db3013](0db3013c9b))
* HSN-wise-summary of outward supplies Updated Report ([3637525](363752510e))
* Ignore loan repayments made from salary slip ([b7e1d40](b7e1d40e43))
* **india:** invoice type for a debit note e-invoice ([#30948](https://github.com/frappe/erpnext/issues/30948)) ([c46add3](c46add3760))
* **india:** keyerror while generating e-way bill from an e-invoice ([#30879](https://github.com/frappe/erpnext/issues/30879)) ([98d799e](98d799e7cc))
* **india:** re-arrange e-way bill dialog fields ([#30941](https://github.com/frappe/erpnext/issues/30941)) ([7ce5c93](7ce5c93f44))
* Item Alternative Test ([964de1f](964de1fc69))
* patch for renaming membership settings ([#30929](https://github.com/frappe/erpnext/issues/30929)) ([9189653](9189653f2e))
* **pos:** creating pos returns resets pricing rules & discounts ([#30936](https://github.com/frappe/erpnext/issues/30936)) ([a0e39db](a0e39db200))
* remove check for already allocated earned leaves ([#30931](https://github.com/frappe/erpnext/issues/30931)) ([#30932](https://github.com/frappe/erpnext/issues/30932)) ([80f7d66](80f7d66255))
* Remove commit from stock entry test. The assertion is not important ([c449b35](c449b35cc1))
* Set available-for-use date if missing ([#30838](https://github.com/frappe/erpnext/issues/30838)) ([bf2eaec](bf2eaecb1d))
* show group warehouse in Sales Order ([#30891](https://github.com/frappe/erpnext/issues/30891)) ([#30893](https://github.com/frappe/erpnext/issues/30893)) ([c458e14](c458e14e68))
* Show linked time sheets in sales invoice dashboard ([4f4af52](4f4af523e0))
* sort before picking next stock reco ([e27fb58](e27fb58130))
* subtract change_amount from paid_amount field on POS Register ([#30937](https://github.com/frappe/erpnext/issues/30937)) ([b7e873b](b7e873b55d))
* Unlink and delete batch created from stock reco on cancel ([fc35323](fc35323106))
* Wrap SLE actual_qty in `flt` to avoid NoneType operation ([d53228b](d53228b153))

### Features

* **HR:** Leave Type configuration to allow over allocation (backport [#30940](https://github.com/frappe/erpnext/issues/30940)) ([#30944](https://github.com/frappe/erpnext/issues/30944)) ([64440fc](64440fc4fb))
* **india:** cancel e-way bill is enabled with e-invoicing APIs. ([#30924](https://github.com/frappe/erpnext/issues/30924)) ([4ef2ffd](4ef2ffd14c))
* **india:** generate qrcode button for e-invoice ([#30946](https://github.com/frappe/erpnext/issues/30946)) ([7bd5b2b](7bd5b2ba29))
* **india:** store e-way bill auto calculated distance in sales invoice ([#30923](https://github.com/frappe/erpnext/issues/30923)) ([b96f156](b96f1565c5))
2022-05-13 18:12:18 +05:30
Frappe PR Bot
8cc2ea0ddb chore(release): Bumped to Version 13.28.0
# [13.28.0](https://github.com/frappe/erpnext/compare/v13.27.1...v13.28.0) (2022-05-03)

### Bug Fixes

* Consistent accounting dimensions across Sales and Purchase docs ([5df5058](5df50588cf))
* convert default_item_manufacturer to link field ([#30835](https://github.com/frappe/erpnext/issues/30835)) ([#30866](https://github.com/frappe/erpnext/issues/30866)) ([37fad7e](37fad7e04c))
* Cost center filter on payment reconciliation ([22e7f03](22e7f03a03))
* filters not working in Shift Assignment Calendar view ([#30822](https://github.com/frappe/erpnext/issues/30822)) ([3cdbb65](3cdbb65b5a))
* Ignore custom field validation while setup ([ee54bf7](ee54bf7fe2))
* **india:** e-invoice generation for registered composition gst category type ([#30814](https://github.com/frappe/erpnext/issues/30814)) ([#30877](https://github.com/frappe/erpnext/issues/30877)) ([246869d](246869dd28))
* Multi currency opening invoices ([2e62d51](2e62d518e8))
* payment days calculation for employees joining/leaving mid-month ([#30863](https://github.com/frappe/erpnext/issues/30863)) ([#30883](https://github.com/frappe/erpnext/issues/30883)) ([a1b0813](a1b0813966))
* Period Closing Voucher is considering GL entries with is_cancelled=1 ([#30865](https://github.com/frappe/erpnext/issues/30865)) ([5a5b49b](5a5b49b61a))
* **pos:** number pad translations ([b01f855](b01f8555e5))
* **pos:** search field doesn't reset on checkout ([edbf551](edbf5513da))
* Supply type for overseas invoices with payment of tax ([fdcc591](fdcc591a5e))
* supply type for sez invoices with payment of tax ([cf08710](cf087103cb))
* Use `account_type == 'Stock'` to filter stock accounts ([93482f3](93482f3302))
* **UX:** misleading stock entry lables ([#30870](https://github.com/frappe/erpnext/issues/30870)) ([#30871](https://github.com/frappe/erpnext/issues/30871)) ([57b03f0](57b03f0bf2))
* **UX:** record reason for skipping attendance or marking absent for auto attendance ([#30846](https://github.com/frappe/erpnext/issues/30846)) ([f7bf4a3](f7bf4a3e62))
* Vat Audit report fixes ([d5319a4](d5319a4826))

### Features

* Copy task color from project template (backport [#30857](https://github.com/frappe/erpnext/issues/30857)) ([#30859](https://github.com/frappe/erpnext/issues/30859)) ([7ee18e8](7ee18e86a2))
* support product bundles in picklist (backport [#30762](https://github.com/frappe/erpnext/issues/30762)) ([#30826](https://github.com/frappe/erpnext/issues/30826)) ([645ee2d](645ee2d822))
2022-05-13 18:12:18 +05:30
Frappe PR Bot
096dcf0e54 chore(release): Bumped to Version 13.27.1
## [13.27.1](https://github.com/frappe/erpnext/compare/v13.27.0...v13.27.1) (2022-04-26)

### Bug Fixes

* Add accounting dimensions for round off GL Entry ([dedb90e](dedb90ea72))
* batch_no filtering not working when batch no is also a number in scientific notation ([#30770](https://github.com/frappe/erpnext/issues/30770)) ([#30771](https://github.com/frappe/erpnext/issues/30771)) ([c339305](c339305e9c))
* Check if accounting dimension exists ([1834671](1834671d59))
* dependent gle reposting (backport [#30726](https://github.com/frappe/erpnext/issues/30726)) ([#30772](https://github.com/frappe/erpnext/issues/30772)) ([a6d0938](a6d0938591))
* Do not validate while creating accounting dimension ([153b41a](153b41a269))
* e_commerce_settings.js ([3a5f5d5](3a5f5d5cd0))
* e_commerce_settings.py ([86c5f4d](86c5f4db85))
* First preference to parent cost center rather than round off cost center ([a2d95fc](a2d95fc62b))
* Handle Multiselect field mapping separately ([dc2f694](dc2f694547))
* **india:** 401 & 403 client error while generating IRN ([198bdcf](198bdcfdc6))
* **india:** cess value not considered while validating e-invoice totals ([#30800](https://github.com/frappe/erpnext/issues/30800)) ([f70fca1](f70fca1c9e))
* **india:** transporter name is null while generating e-way bill ([#30736](https://github.com/frappe/erpnext/issues/30736)) ([6291b28](6291b28c37))
* linter ([6dddbb9](6dddbb9f27))
* Loan doctypes in bank reconciliation ([e69c715](e69c71576d))
* Mistyped variable name in patch ([e76220e](e76220e819))
* monthly attendance sheet ([#30748](https://github.com/frappe/erpnext/issues/30748)) ([0b4e3f1](0b4e3f1467))
* Must not be able to start Job Card if it is related to Work Order that is not started yet ([#29072](https://github.com/frappe/erpnext/issues/29072)) ([#30755](https://github.com/frappe/erpnext/issues/30755)) ([b656ffa](b656ffa45e))
* Query filter fields from Website Item instead of Item master ([bed9e09](bed9e09153))
* select doctype as payment_document ([44f0b69](44f0b69152))
* **Selling,E-Commerce:** Shopping cart quotation without website item. ([#29085](https://github.com/frappe/erpnext/issues/29085)) ([ea0fe5e](ea0fe5e10c))
* SO analysis rpt will fetch SO's without Delivery note as well ([f9d89c7](f9d89c7ce6))
* translation ([#30781](https://github.com/frappe/erpnext/issues/30781)) ([#30783](https://github.com/frappe/erpnext/issues/30783)) ([8335ca6](8335ca6331))
* Update token to allow updates on protected branch ([baab379](baab3797ca))
* update translation ([#30725](https://github.com/frappe/erpnext/issues/30725)) ([#30776](https://github.com/frappe/erpnext/issues/30776)) ([b585262](b585262842))
* Use parent cost center for Sales and Purchase Invoice ([fe9f329](fe9f32946c))
* Use right precision for asset value after full schedule ([#30745](https://github.com/frappe/erpnext/issues/30745)) ([269e192](269e1923c9))
* Validate field filter wrt to Website Item & re-use validation in Item Group ([34437a8](34437a83df))
2022-05-13 18:12:18 +05:30
Deepesh Garg
f2cbb70325 fix: Multiple fixes in GSTR-1 report 2022-05-13 18:12:18 +05:30
Deepesh Garg
7d8e3344e9 Merge pull request #31002 from frappe/mergify/bp/version-13-hotfix/pr-30990
fix: Item rate reset on changing posting date (backport #30990)
2022-05-13 14:44:04 +05:30
Marica
f7eb3ca1c0 Merge pull request #31000 from frappe/mergify/bp/version-13-hotfix/pr-30894
chore: added RFQ Link to Opportunity Dashboard (backport #30894)
2022-05-13 13:17:12 +05:30
Ankush Menat
0ec5adeed8 chore:conflicts 2022-05-13 13:14:08 +05:30
Marica
44d892fb70 Merge pull request #30999 from frappe/mergify/bp/version-13-hotfix/pr-30614
feat: add Link to Opportunity (backport #30614)
2022-05-13 13:12:50 +05:30
Frappe PR Bot
9ea017248d chore(release): Bumped to Version 13.29.2
## [13.29.2](https://github.com/frappe/erpnext/compare/v13.29.1...v13.29.2) (2022-05-13)

### Bug Fixes

* **patch:** avoid checking for return field if it doesnt exits (backport [#30995](https://github.com/frappe/erpnext/issues/30995)) ([#30997](https://github.com/frappe/erpnext/issues/30997)) ([752bda5](752bda5d84))
2022-05-13 07:41:38 +00:00
Ankush Menat
e392f6ce31 Merge branch 'version-13-pre-release' into version-13 2022-05-13 13:09:44 +05:30
mergify[bot]
752bda5d84 fix(patch): avoid checking for return field if it doesnt exits (backport #30995) (#30997)
(cherry picked from commit b08180092e)

Co-authored-by: Ankush Menat <me@ankush.dev>
2022-05-13 13:09:22 +05:30
Deepesh Garg
8ef649f65d fix: Item rate reset on changing posting date (#30990)
* fix: Item rate reset on changing posting date

* chore: Remove debugger

(cherry picked from commit 54d6cf18fc)

# Conflicts:
#	erpnext/public/js/controllers/transaction.js
2022-05-13 07:37:57 +00:00
mergify[bot]
a94b5c0d8b fix(patch): avoid checking for return field if it doesnt exits (backport #30995) (#30997)
(cherry picked from commit b08180092e)

Co-authored-by: Ankush Menat <me@ankush.dev>
2022-05-13 13:07:51 +05:30
Marica
a829072353 Merge pull request #31001 from frappe/mergify/bp/version-13-hotfix/pr-30911
fix: German translations for Employee doctype (backport #30911)
2022-05-13 12:55:11 +05:30
Marica
eb1245e9f8 Merge pull request #30998 from frappe/mergify/bp/version-13-hotfix/pr-30818
feat: request_for_quotation - show supplier name (backport #30818)
2022-05-13 12:24:02 +05:30
barredterra
b9bda04a83 fix: german translations for Employee
(cherry picked from commit 02b38a439f)
2022-05-13 06:54:02 +00:00
Wolfram Schmidt
24851a8577 chore: added RFQ Link to Opportunity Dashboard (#30894)
added Link to Dashboard

added backlink to Request for Quotation in the Quotation dashboard for transparency and ease of navigation.

(cherry picked from commit c4a2778359)
2022-05-13 06:48:34 +00:00
Wolfram Schmidt
bc23bc738e feat: add Link to Opportunity (#30614)
Adding Link to Opportunity

Following the process of Opportunity to Quotation I added a section and link field to Opportunity for a backlink when using "create"-> Request for Quotation from Opportunity.

(cherry picked from commit 0bbed414f8)
2022-05-13 06:48:08 +00:00
Ahmad
b6a3e693ae feat: request_for_quotation - refactor
- Set supplier as link in while selecting supplier to create quotation

(cherry picked from commit 04c96b547e)
2022-05-13 06:47:19 +00:00
Ahmad
db4e264d92 feat: request_for_quotation
- Show supplier name if supplier ID is not equal to supplier name

(cherry picked from commit b226e7d2ac)
2022-05-13 06:47:18 +00:00
Saqib Ansari
1ecbf33960 Merge pull request #30991 from frappe/mergify/bp/version-13-hotfix/pr-30968
fix(accounts): minor fixes & validations (backport #30968)
2022-05-13 12:03:52 +05:30
Saqib Ansari
013ac26231 test: sales_invoice_with_disabled_account
(cherry picked from commit 6c16422beb)
2022-05-12 16:37:27 +00:00
Saqib Ansari
7c750571cd test: payment_entry_against_onhold_purchase_invoice
(cherry picked from commit 92613777b9)
2022-05-12 16:37:26 +00:00
Saqib Ansari
515e49bb90 fix: validate disabled accounts before posting ledger entries
(cherry picked from commit 95b059a98c)
2022-05-12 16:37:26 +00:00
Saqib Ansari
047c879bec fix: user can select disabled accounts in taxes table
(cherry picked from commit a1e3ae8869)
2022-05-12 16:37:25 +00:00
Saqib Ansari
9fbd170fa4 fix: validate on hold purchase invoices in payment entry
(cherry picked from commit b0f302e579)
2022-05-12 16:37:24 +00:00
Frappe PR Bot
a89612e448 chore(release): Bumped to Version 13.29.1
## [13.29.1](https://github.com/frappe/erpnext/compare/v13.29.0...v13.29.1) (2022-05-12)

### Bug Fixes

* Failing accounting dimension patch ([b9a5575](b9a557599e))
2022-05-12 10:01:53 +00:00
Deepesh Garg
1def38dba0 Merge pull request #30985 from frappe/version-13-pre-release
chore: version-13 patch release
2022-05-12 15:29:21 +05:30
Deepesh Garg
bed1a218a2 Merge pull request #30984 from frappe/mergify/bp/version-13-pre-release/pr-30982
fix: Failing accounting dimension patch (backport #30982)
2022-05-12 15:28:05 +05:30
Deepesh Garg
b9a557599e fix: Failing accounting dimension patch
(cherry picked from commit b14a7b8a5d)
2022-05-12 09:48:53 +00:00
Deepesh Garg
43ebe946f1 Merge pull request #30982 from deepeshgarg007/failing_patch_for_dimensisons
fix: Failing accounting dimension patch
2022-05-12 14:57:01 +05:30
Deepesh Garg
b14a7b8a5d fix: Failing accounting dimension patch 2022-05-12 13:54:47 +05:30
mergify[bot]
295ffb3f1a fix: stock analytics report shows incorrect data there's no stock movement in a period (backport #30945) (#30980)
* test: basic test for stock analytics report

(cherry picked from commit d81422fb58)

* fix: consider previous balance is missing

Also remove `total`, total of total is a meaningless value.

(cherry picked from commit 6ab0046e9c)

* fix: batch_no doesn't maintain qty_after_transaction

(cherry picked from commit 287b255ad6)

* fix: only carry-forward balances till today's period

Showing data in future doesn't make sense. Only carry-forward till last
bucket that contains today's day.

(cherry picked from commit 198b91f8d4)

Co-authored-by: Ankush Menat <me@ankush.dev>
2022-05-12 13:06:52 +05:30
Marica
d697515125 Merge pull request #30975 from frappe/mergify/bp/version-13-hotfix/pr-30950
fix: Set actual qty and basic rate in SE on warehouse triggers (`get_warehouse_details`) (backport #30950)
2022-05-12 12:54:12 +05:30
marination
6ef1261afd test: Test for mapped SE and fix for failing tests
- Remove `set_missing_values` from mapper util function since a lot of item data is set after this function is run
- `split_batch` can skip `set_missing_values` since no warehouses are set on mapping and relies on user input

(cherry picked from commit 4fa15b50ca)
2022-05-11 14:44:42 +00:00
marination
fe52c1f354 fix: set_missing_values in SE and re-use the same on all SE mappings
- `set_missing_values` in SE will set actual qty, transfer qty and calculate rate/amount
- Re-use `set_missing_values` wherever SE is doc is being mapped

(cherry picked from commit 90a8e924f5)
2022-05-11 14:44:42 +00:00
marination
678a01d4bd fix: Calculate totals even though pricing rule is not applied on mapped doc
- `apply_pricing_rule` is triggered due to change in some data which most likely contributes to Total.

(cherry picked from commit 494ddd1eb4)
2022-05-11 14:44:38 +00:00
marination
30b0aee013 fix: Set actual qty and basic rate in SE on warehouse triggers (get_warehouse_details)
- set `actual_qty` on source and target warehouse change

(cherry picked from commit 1ce45f623e)
2022-05-11 14:44:37 +00:00
mergify[bot]
97ea1f5123 fix: per_billed for return DN (backport #30868) (#30971)
This is a semi-automatic backport of pull request #30868 done by [Mergify](https://mergify.com).
2022-05-11 14:33:58 +00:00
rohitwaghchaure
60964a48c4 Merge pull request #30947 from frappe/mergify/bp/version-13-hotfix/pr-30938
fix: allow to use formatting for the field to_discuss in opportunity (backport #30938)
2022-05-11 17:48:23 +05:30
rohitwaghchaure
87fd93370a fix: conflicts 2022-05-11 16:07:24 +05:30
rohitwaghchaure
fb62bbf61a fix: conflicts 2022-05-11 16:05:19 +05:30
Ankush Menat
b008efbfdb Merge pull request #30970 from frappe/mergify/bp/version-13-hotfix/pr-30969
chore: fifo queue vs qty after transaction comparison report (backport #30969)
2022-05-11 15:55:17 +05:30
Ankush Menat
78f694ca4e chore: incompatible changes 2022-05-11 15:22:33 +05:30
Ankush Menat
8c2bfe1d5c chore: fifo queue vs qty after transaction comparison report
(cherry picked from commit f6d6463a33)
2022-05-11 09:43:15 +00:00
Marica
c873950f41 Merge pull request #30967 from frappe/mergify/bp/version-13-hotfix/pr-30962
fix: Block 0 Qty via Update Items to be consistent with form validation (backport #30962)
2022-05-11 13:04:10 +05:30
marination
56478752e4 fix: Block 0 Qty via Update Items to be consistent with form validation
(cherry picked from commit 0c9154389b)
2022-05-11 07:12:29 +00:00
gavin
a761c5c62c Merge pull request #30961 from frappe/mergify/bp/version-13-hotfix/pr-30958
fix(charts): Pass fieldtype for chart data in selling reports (backport #30958)
2022-05-10 17:55:19 +05:30
Gavin D'souza
917e7c34af fix(charts): Pass fieldtype for chart data in selling reports
(cherry picked from commit 7bf0e4f8e5)
2022-05-10 12:10:48 +00:00
Frappe PR Bot
974f2a85ea chore(release): Bumped to Version 13.29.0
# [13.29.0](https://github.com/frappe/erpnext/compare/v13.28.0...v13.29.0) (2022-05-10)

### Bug Fixes

* Consider paryt and party type as well in group by consolidated view ([189fc89](189fc89e2d))
* disable form save on naming series tool ([#30909](https://github.com/frappe/erpnext/issues/30909)) ([#30910](https://github.com/frappe/erpnext/issues/30910)) ([d60a6cb](d60a6cb2f8))
* double future qty updates ([0db3013](0db3013c9b))
* HSN-wise-summary of outward supplies Updated Report ([3637525](363752510e))
* Ignore loan repayments made from salary slip ([b7e1d40](b7e1d40e43))
* **india:** invoice type for a debit note e-invoice ([#30948](https://github.com/frappe/erpnext/issues/30948)) ([c46add3](c46add3760))
* **india:** keyerror while generating e-way bill from an e-invoice ([#30879](https://github.com/frappe/erpnext/issues/30879)) ([98d799e](98d799e7cc))
* **india:** re-arrange e-way bill dialog fields ([#30941](https://github.com/frappe/erpnext/issues/30941)) ([7ce5c93](7ce5c93f44))
* Item Alternative Test ([964de1f](964de1fc69))
* patch for renaming membership settings ([#30929](https://github.com/frappe/erpnext/issues/30929)) ([9189653](9189653f2e))
* **pos:** creating pos returns resets pricing rules & discounts ([#30936](https://github.com/frappe/erpnext/issues/30936)) ([a0e39db](a0e39db200))
* remove check for already allocated earned leaves ([#30931](https://github.com/frappe/erpnext/issues/30931)) ([#30932](https://github.com/frappe/erpnext/issues/30932)) ([80f7d66](80f7d66255))
* Remove commit from stock entry test. The assertion is not important ([c449b35](c449b35cc1))
* Set available-for-use date if missing ([#30838](https://github.com/frappe/erpnext/issues/30838)) ([bf2eaec](bf2eaecb1d))
* show group warehouse in Sales Order ([#30891](https://github.com/frappe/erpnext/issues/30891)) ([#30893](https://github.com/frappe/erpnext/issues/30893)) ([c458e14](c458e14e68))
* Show linked time sheets in sales invoice dashboard ([4f4af52](4f4af523e0))
* sort before picking next stock reco ([e27fb58](e27fb58130))
* subtract change_amount from paid_amount field on POS Register ([#30937](https://github.com/frappe/erpnext/issues/30937)) ([b7e873b](b7e873b55d))
* Unlink and delete batch created from stock reco on cancel ([fc35323](fc35323106))
* Wrap SLE actual_qty in `flt` to avoid NoneType operation ([d53228b](d53228b153))

### Features

* **HR:** Leave Type configuration to allow over allocation (backport [#30940](https://github.com/frappe/erpnext/issues/30940)) ([#30944](https://github.com/frappe/erpnext/issues/30944)) ([64440fc](64440fc4fb))
* **india:** cancel e-way bill is enabled with e-invoicing APIs. ([#30924](https://github.com/frappe/erpnext/issues/30924)) ([4ef2ffd](4ef2ffd14c))
* **india:** generate qrcode button for e-invoice ([#30946](https://github.com/frappe/erpnext/issues/30946)) ([7bd5b2b](7bd5b2ba29))
* **india:** store e-way bill auto calculated distance in sales invoice ([#30923](https://github.com/frappe/erpnext/issues/30923)) ([b96f156](b96f1565c5))
2022-05-10 10:27:19 +00:00
Deepesh Garg
705a21a0f0 Merge pull request #30952 from frappe/version-13-pre-release
chore: Release for version-13
2022-05-10 15:55:39 +05:30
Ankush Menat
6863a59472 Merge branch 'version-13-hotfix' into version-13-pre-release 2022-05-10 15:20:35 +05:30
Ankush Menat
f64ba80913 Merge pull request #30953 from frappe/mergify/bp/version-13-hotfix/pr-30913
fix: double future qty updates (backport #30913)
2022-05-10 15:16:37 +05:30
mergify[bot]
b7e873b55d fix: subtract change_amount from paid_amount field on POS Register (#30937)
Co-authored-by: HarryPaulo <paulo_fabris@hotmail.com>
Co-authored-by: Saqib Ansari <nextchamp.saqib@gmail.com>
2022-05-10 14:54:19 +05:30
Ankush Menat
4d682face2 chore: remove datettime formatting from debug report
This hides some information that would otherwise help during debugging

(cherry picked from commit ae842d8145)
2022-05-10 14:51:02 +05:30
Ankush Menat
6312938fed chore: conflicts 2022-05-10 14:50:35 +05:30
Ankush Menat
e27fb58130 fix: sort before picking next stock reco
(cherry picked from commit 7e2fbc050a)
2022-05-10 14:50:35 +05:30
Ankush Menat
0db3013c9b fix: double future qty updates
update_qty_in_future_sle is reprocessing rows which are already
processed by process_sle_against_current_voucher

(cherry picked from commit 7c839c4503)

# Conflicts:
#	erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
2022-05-10 09:17:34 +00:00
Deepesh Garg
a85fe517df Merge pull request #30951 from frappe/version-13-hotfix
chore: Pre release for version-13
2022-05-10 14:42:16 +05:30
Deepesh Garg
3373ce5c47 Merge pull request #30949 from frappe/mergify/bp/version-13-hotfix/pr-30948
fix(india): invoice type for a debit note e-invoice (backport #30948)
2022-05-10 14:13:48 +05:30
Saqib Ansari
c46add3760 fix(india): invoice type for a debit note e-invoice (#30948)
(cherry picked from commit 8dd046cc51)
2022-05-10 08:10:34 +00:00
mergify[bot]
7bd5b2ba29 feat(india): generate qrcode button for e-invoice (#30946) 2022-05-10 12:29:52 +05:30
Rohit Waghchaure
e126d4e592 fix: allow to use formatting for the field to_discuss in opportunity
(cherry picked from commit 3f41cb762d)

# Conflicts:
#	erpnext/crm/doctype/opportunity/opportunity.py
#	erpnext/crm/doctype/opportunity/test_opportunity.py
2022-05-10 06:41:34 +00:00
mergify[bot]
64440fc4fb feat(HR): Leave Type configuration to allow over allocation (backport #30940) (#30944)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-05-10 09:25:32 +05:30
mergify[bot]
7ce5c93f44 fix(india): re-arrange e-way bill dialog fields (#30941) 2022-05-09 21:36:57 +05:30
Ankush Menat
c1f6586e39 Merge pull request #30869 from marination/batch-source-reference-v13
fix: Unlink and delete batch created from stock reco on cancel
2022-05-09 19:01:59 +05:30
mergify[bot]
a0e39db200 fix(pos): creating pos returns resets pricing rules & discounts (#30936) 2022-05-09 17:43:36 +05:30
mergify[bot]
80f7d66255 fix: remove check for already allocated earned leaves (#30931) (#30932)
* fix: remove check for already allocated earned leaves

* fix: do not set New Leaves Allocated field as read-only for earned leaves

- removing this until there's a better way to update existing allocations

(cherry picked from commit f92bc4dd33)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-05-09 12:34:21 +05:30
Rucha Mahabal
9189653f2e fix: patch for renaming membership settings (#30929) 2022-05-09 10:11:52 +05:30
Deepesh Garg
4e53dd71db Merge pull request #30065 from govindsmenokee/patch-9
fix: HSN-wise-summary of outward supplies Updated Report
2022-05-08 19:36:11 +05:30
mergify[bot]
4ef2ffd14c feat(india): cancel e-way bill is enabled with e-invoicing APIs. (#30924) 2022-05-08 19:11:04 +05:30
mergify[bot]
b96f1565c5 feat(india): store e-way bill auto calculated distance in sales invoice (#30923) 2022-05-08 19:10:22 +05:30
Saqib Ansari
713a84bb73 Merge branch 'version-13-hotfix' into patch-9 2022-05-08 12:49:28 +05:30
Saqib Ansari
2972ef8f2c chore: fix linter (#30925) 2022-05-08 12:48:57 +05:30
Deepesh Garg
a8fbd2451b chore: Remove print statement 2022-05-07 22:53:59 +05:30
Deepesh Garg
b0e929b8ae chore: Remove extra columns 2022-05-07 22:44:53 +05:30
Deepesh Garg
ac362c75db Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into patch-9 2022-05-07 19:04:14 +05:30
Ganga Manoj
bf2eaecb1d fix: Set available-for-use date if missing (#30838) 2022-05-06 18:06:21 +05:30
Deepesh Garg
e9e2dcc1bf Merge pull request #30915 from frappe/mergify/bp/version-13-hotfix/pr-30897
fix: Consider party and party type as well in group by consolidated view (backport #30897)
2022-05-06 13:37:54 +05:30
Deepesh Garg
189fc89e2d fix: Consider paryt and party type as well in group by consolidated view
(cherry picked from commit c2d52a1ac0)
2022-05-06 07:40:39 +00:00
Deepesh Garg
4c2fdfe2c1 Merge pull request #30914 from deepeshgarg007/payment_reco_linting_issue
chore: Linting issues
2022-05-06 13:08:57 +05:30
Deepesh Garg
b7698aa210 chore: Linting issues 2022-05-06 13:08:09 +05:30
Deepesh Garg
96db9ee17b Merge pull request #30912 from frappe/mergify/bp/version-13-hotfix/pr-30879
fix(india): keyerror while generating e-way bill from an e-invoice (backport #30879)
2022-05-06 13:05:21 +05:30
maharshivpatel
98d799e7cc fix(india): keyerror while generating e-way bill from an e-invoice (#30879)
(cherry picked from commit ee7a7eb782)
2022-05-06 05:53:50 +00:00
mergify[bot]
d60a6cb2f8 fix: disable form save on naming series tool (#30909) (#30910)
(cherry picked from commit f31122cbc3)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-05-05 17:38:11 +05:30
marination
a2fff8741e style: Spaces to Tabs 2022-05-05 13:00:00 +05:30
mergify[bot]
c458e14e68 fix: show group warehouse in Sales Order (#30891) (#30893)
(cherry picked from commit 91cd5f5d4a)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-05-05 11:07:11 +05:30
Deepesh Garg
59686093b4 Merge pull request #30902 from frappe/mergify/bp/version-13-hotfix/pr-30899
fix: Show linked time sheets in sales invoice dashboard (backport #30899)
2022-05-04 21:01:08 +05:30
Deepesh Garg
4f4af523e0 fix: Show linked time sheets in sales invoice dashboard
(cherry picked from commit 3e38dc7ea8)
2022-05-04 14:24:57 +00:00
marination
c449b35cc1 fix: Remove commit from stock entry test. The assertion is not important 2022-05-04 13:00:00 +05:30
Deepesh Garg
57409b6ffd Merge pull request #30884 from deepeshgarg007/loan_repayment_clearance_v3
fix: Ignore loan repayments made from salary slip
2022-05-04 09:44:22 +05:30
Frappe PR Bot
53024be347 chore(release): Bumped to Version 13.28.0
# [13.28.0](https://github.com/frappe/erpnext/compare/v13.27.1...v13.28.0) (2022-05-03)

### Bug Fixes

* Consistent accounting dimensions across Sales and Purchase docs ([5df5058](5df50588cf))
* convert default_item_manufacturer to link field ([#30835](https://github.com/frappe/erpnext/issues/30835)) ([#30866](https://github.com/frappe/erpnext/issues/30866)) ([37fad7e](37fad7e04c))
* Cost center filter on payment reconciliation ([22e7f03](22e7f03a03))
* filters not working in Shift Assignment Calendar view ([#30822](https://github.com/frappe/erpnext/issues/30822)) ([3cdbb65](3cdbb65b5a))
* Ignore custom field validation while setup ([ee54bf7](ee54bf7fe2))
* **india:** e-invoice generation for registered composition gst category type ([#30814](https://github.com/frappe/erpnext/issues/30814)) ([#30877](https://github.com/frappe/erpnext/issues/30877)) ([246869d](246869dd28))
* Multi currency opening invoices ([2e62d51](2e62d518e8))
* payment days calculation for employees joining/leaving mid-month ([#30863](https://github.com/frappe/erpnext/issues/30863)) ([#30883](https://github.com/frappe/erpnext/issues/30883)) ([a1b0813](a1b0813966))
* Period Closing Voucher is considering GL entries with is_cancelled=1 ([#30865](https://github.com/frappe/erpnext/issues/30865)) ([5a5b49b](5a5b49b61a))
* **pos:** number pad translations ([b01f855](b01f8555e5))
* **pos:** search field doesn't reset on checkout ([edbf551](edbf5513da))
* Supply type for overseas invoices with payment of tax ([fdcc591](fdcc591a5e))
* supply type for sez invoices with payment of tax ([cf08710](cf087103cb))
* Use `account_type == 'Stock'` to filter stock accounts ([93482f3](93482f3302))
* **UX:** misleading stock entry lables ([#30870](https://github.com/frappe/erpnext/issues/30870)) ([#30871](https://github.com/frappe/erpnext/issues/30871)) ([57b03f0](57b03f0bf2))
* **UX:** record reason for skipping attendance or marking absent for auto attendance ([#30846](https://github.com/frappe/erpnext/issues/30846)) ([f7bf4a3](f7bf4a3e62))
* Vat Audit report fixes ([d5319a4](d5319a4826))

### Features

* Copy task color from project template (backport [#30857](https://github.com/frappe/erpnext/issues/30857)) ([#30859](https://github.com/frappe/erpnext/issues/30859)) ([7ee18e8](7ee18e86a2))
* support product bundles in picklist (backport [#30762](https://github.com/frappe/erpnext/issues/30762)) ([#30826](https://github.com/frappe/erpnext/issues/30826)) ([645ee2d](645ee2d822))
2022-05-03 08:51:32 +00:00
Deepesh Garg
a1f31013e2 Merge pull request #30886 from frappe/version-13-pre-release
chore: weekly release for version-13
2022-05-03 14:19:46 +05:30
Deepesh Garg
64d96f436b Merge pull request #30885 from frappe/version-13-hotfix
chore: pre release for version-13
2022-05-03 13:41:33 +05:30
Deepesh Garg
fda250606a Merge branch 'version-13-pre-release' into version-13-hotfix 2022-05-03 13:21:10 +05:30
Deepesh Garg
2126dee8ca Merge pull request #30874 from frappe/mergify/bp/version-13-hotfix/pr-30848
fix: Cost center filter in payment reconciliation (backport #30848)
2022-05-03 13:18:31 +05:30
marination
d53228b153 fix: Wrap SLE actual_qty in flt to avoid NoneType operation
- Since Batch cancellation SLEs do not set qtys (will fix separately), `merge_similar_entries` gets `actual_qty` as None
- This causes NoneType operation error on tests that cancel batch-serial reco
- Modified tests to avoid using commit and rollback explicitly
2022-05-03 13:00:00 +05:30
Deepesh Garg
b7e1d40e43 fix: Ignore loan repayments made from salary slip 2022-05-03 12:55:04 +05:30
Deepesh Garg
937d358fbf Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-30848 2022-05-03 12:46:10 +05:30
Deepesh Garg
366cd6171c chore: resolve conflicts 2022-05-03 12:21:06 +05:30
mergify[bot]
a1b0813966 fix: payment days calculation for employees joining/leaving mid-month (#30863) (#30883)
(cherry picked from commit 924cf7763e)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-05-03 11:46:46 +05:30
Saqib Ansari
4db588e72e Merge pull request #30876 from frappe/mergify/bp/version-13-hotfix/pr-30875
fix(India): Supply type for overseas invoices with payment of tax (backport #30875)
2022-05-02 22:17:56 +05:30
mergify[bot]
246869dd28 fix(india): e-invoice generation for registered composition gst category type (#30814) (#30877) 2022-05-02 22:16:43 +05:30
Saqib Ansari
cf087103cb fix: supply type for sez invoices with payment of tax
(cherry picked from commit c8aa77285e)
2022-05-02 16:20:44 +00:00
Deepesh Garg
fdcc591a5e fix: Supply type for overseas invoices with payment of tax
(cherry picked from commit d7cb269e0c)
2022-05-02 16:20:44 +00:00
Deepesh Garg
0df96e1084 test: Add test for payment reconciliation
(cherry picked from commit b440dabe12)
2022-05-02 12:29:25 +00:00
Deepesh Garg
22e7f03a03 fix: Cost center filter on payment reconciliation
(cherry picked from commit ab94b73e93)

# Conflicts:
#	erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js
2022-05-02 12:29:24 +00:00
Deepesh Garg
6aab9ca427 Merge pull request #30852 from deepeshgarg007/vat_ksa
fix: Vat Audit report fixes
2022-05-02 16:14:03 +05:30
mergify[bot]
57b03f0bf2 fix(UX): misleading stock entry lables (#30870) (#30871)
* fix(UX): misleading stock entry lables

* chore: field labels

[skip ci]

Co-authored-by: Marica <maricadsouza221197@gmail.com>

Co-authored-by: Marica <maricadsouza221197@gmail.com>
(cherry picked from commit 59a5090843)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-05-02 15:17:13 +05:30
Sherin KR
5a5b49b61a fix: Period Closing Voucher is considering GL entries with is_cancelled=1 (#30865)
* fix: Period Closing Voucher - Period Closing Voucher is considering GL entry with is_cancelled=1 as well

* fix: condition

Co-authored-by: Saqib Ansari <nextchamp.saqib@gmail.com>
2022-05-02 15:03:30 +05:30
mergify[bot]
37fad7e04c fix: convert default_item_manufacturer to link field (#30835) (#30866)
(cherry picked from commit dcda55641b)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-05-02 14:58:05 +05:30
marination
964de1fc69 fix: Item Alternative Test 2022-05-02 14:44:26 +05:30
marination
5bc5af1066 chore: Tests for Stock Reconciliation 2022-05-02 14:41:08 +05:30
marination
a144548db9 chore: Tests for Stock Entry 2022-05-02 14:18:54 +05:30
marination
fc35323106 fix: Unlink and delete batch created from stock reco on cancel 2022-05-02 14:18:27 +05:30
Deepesh Garg
7c0069c0d3 chore: Education domain deprecation warning (#30861)
* chore: Education domain deprecation warning

* chore: Education domain deprecation warning

* chore: Rename file and patch
2022-05-02 13:29:25 +05:30
Deepesh Garg
0b9a59c605 Merge pull request #30856 from frappe/mergify/bp/version-13-hotfix/pr-30806
fix: Consistent accounting dimensions across Sales and Purchase docs (backport #30806)
2022-05-02 11:44:52 +05:30
Deepesh Garg
b9c326e3f6 chore: resolve conflicts 2022-05-02 11:16:48 +05:30
Deepesh Garg
d9756d54ad chore: resolve conflicts 2022-05-02 11:15:58 +05:30
mergify[bot]
7ee18e86a2 feat: Copy task color from project template (backport #30857) (#30859)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
Co-authored-by: sersaber <93864988+sersaber@users.noreply.github.com>
2022-05-02 10:59:58 +05:30
Deepesh Garg
5df50588cf fix: Consistent accounting dimensions across Sales and Purchase docs
(cherry picked from commit 82a0635c66)

# Conflicts:
#	erpnext/patches.txt
#	erpnext/selling/doctype/sales_order/sales_order.json
2022-05-01 16:33:16 +00:00
Deepesh Garg
87383680f0 Merge pull request #30853 from frappe/mergify/bp/version-13-hotfix/pr-30815
fix: Ignore custom field validation while setup (backport #30815)
2022-05-01 15:22:57 +05:30
Deepesh Garg
ee54bf7fe2 fix: Ignore custom field validation while setup
(cherry picked from commit 67bb29026f)
2022-04-30 16:33:44 +00:00
Deepesh Garg
d5319a4826 fix: Vat Audit report fixes 2022-04-30 22:01:05 +05:30
Deepesh Garg
a98263cd31 Merge pull request #30845 from frappe/mergify/bp/version-13-hotfix/pr-30843
fix: Multi currency opening invoices (backport #30843)
2022-04-29 23:04:03 +05:30
Deepesh Garg
a3de0320aa Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-30843 2022-04-29 22:16:52 +05:30
Rucha Mahabal
f7bf4a3e62 fix(UX): record reason for skipping attendance or marking absent for auto attendance (#30846) 2022-04-29 16:37:26 +05:30
Deepesh Garg
2e62d518e8 fix: Multi currency opening invoices
(cherry picked from commit a8452c2ba2)
2022-04-29 09:46:34 +00:00
mergify[bot]
731107d0a3 test: create new item instead of using with _Test Item (backport #30827) (#30829)
This is an automatic backport of pull request #30827 done by [Mergify](https://mergify.com).


---


<details>
<summary>Mergify commands and options</summary>

<br />

More conditions and actions can be found in the [documentation](https://docs.mergify.com/).

You can also trigger Mergify actions by commenting on this pull request:

- `@Mergifyio refresh` will re-evaluate the rules
- `@Mergifyio rebase` will rebase this PR on its base branch
- `@Mergifyio update` will merge the base branch into this PR
- `@Mergifyio backport <destination>` will backport this PR on `<destination>` branch

Additionally, on Mergify [dashboard](https://dashboard.mergify.com/) you can:

- look at your merge queues
- generate the Mergify configuration with the config editor.

Finally, you can contact us on https://mergify.com
</details>
2022-04-28 10:58:24 +00:00
mergify[bot]
645ee2d822 feat: support product bundles in picklist (backport #30762) (#30826)
* refactor: misc pick list refactors

- make tracking fields read only and no-copy 🤦
- collapse print settings section, most users configure it once and
  forget about it, not need to show this.
- call pick list grouping function directly
- use get_descendants_of instead of obscure db function

(cherry picked from commit 5c3f9019cc)

* test: bundles in picklist

(cherry picked from commit 7d5682020a)

# Conflicts:
#	erpnext/stock/doctype/pick_list/test_pick_list.py

* feat: Pick list from SO with Product Bundle

(cherry picked from commit 36c5e8a14f)

* refactor: sales order status update

- rename badly named variables
- support updated packed items

(cherry picked from commit e64cc66df7)

# Conflicts:
#	erpnext/stock/doctype/pick_list/pick_list.py

* perf: single update per Sales Order.

For each SO item the sales order picking status was being updated, this
isn't required and wasteful.

(cherry picked from commit c3fc0a4f55)

* feat: back-update min picked qty for a bundle

(cherry picked from commit 60bc26fdbe)

* refactor: simplify needlessly complicated code

(cherry picked from commit 3ddad6891a)

* refactor: groupby using keys instead of int index

(cherry picked from commit 277b51b404)

* refactor: simpler check for non-SO items

(cherry picked from commit f574121741)

* feat: create DN from pick list with bundle items

(cherry picked from commit 23cb0d684d)

* refactor: remove unnecssary vars

also remove misleading docstring

(cherry picked from commit 25485edfd9)

* fix: round off bundle qty

This is to accomodate bundles that might allow floating point qty.

(cherry picked from commit 41aa4b3524)

* feat: transfer picklist stock info to packing list

(cherry picked from commit 1ac275ce61)

* test: product bundle fixture

(cherry picked from commit ee54ece8fd)

* test: test bundle - picklist behaviour

(cherry picked from commit 9e60acdf56)

# Conflicts:
#	erpnext/stock/doctype/pick_list/test_pick_list.py

* fix: compare against stock qty while validating

Other changes:

- only allow whole number of bundles to get picked

(cherry picked from commit 8207697e43)

* fix: dont map picked qty and consider pick qty for new PL

Co-Authored-By: marination <maricadsouza221197@gmail.com>
(cherry picked from commit 47e1a0104c)

* fix(UX): only show pick list when picking is pending

[skip ci]

(cherry picked from commit 9a8e3ef235)

* chore: make picked qty read only

(cherry picked from commit ebd5f0b1bb)

* chore: conflicts and py3.7 compatibilty

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-27 17:08:37 +05:30
mergify[bot]
b45db347cc ci: failfast when merge conflict exists (#30823) (#30824)
[skip ci]

(cherry picked from commit 3ae9fa98c4)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-27 14:45:21 +05:30
Rucha Mahabal
3cdbb65b5a fix: filters not working in Shift Assignment Calendar view (#30822) 2022-04-27 14:44:25 +05:30
Saqib Ansari
f693d43cda Merge pull request #30809 from frappe/mergify/bp/version-13-hotfix/pr-30808
fix(pos): search field doesn't reset on checkout (backport #30808)
2022-04-27 14:15:07 +05:30
Ankush Menat
b33317e987 chore: format 2022-04-26 18:44:03 +05:30
Ankush Menat
980a87417b Merge pull request #30812 from frappe/mergify/bp/version-13-hotfix/pr-30811
chore: Warn users about multiple warehouse accounts (backport #30811)
2022-04-26 18:42:10 +05:30
marination
93482f3302 fix: Use account_type == 'Stock' to filter stock accounts
(cherry picked from commit e2a163d4e9)
2022-04-26 12:49:00 +00:00
marination
38fbd94ac9 chore: Warn users about multiple warehouse accounts
- Get distinct accounts that warehouse value has been booked against
- If same account as the one being set, ignore
- If not same account or multiple accounts: warn user that it makes it harder to track mismatches

(cherry picked from commit 44331f4f1f)
2022-04-26 12:49:00 +00:00
Saqib Ansari
b01f8555e5 fix(pos): number pad translations
(cherry picked from commit b1ac5ff9d2)
2022-04-26 09:18:11 +00:00
Saqib Ansari
edbf5513da fix(pos): search field doesn't reset on checkout
(cherry picked from commit 03a6103fe5)
2022-04-26 09:18:11 +00:00
Frappe PR Bot
b77b3780d6 chore(release): Bumped to Version 13.27.1
## [13.27.1](https://github.com/frappe/erpnext/compare/v13.27.0...v13.27.1) (2022-04-26)

### Bug Fixes

* Add accounting dimensions for round off GL Entry ([dedb90e](dedb90ea72))
* batch_no filtering not working when batch no is also a number in scientific notation ([#30770](https://github.com/frappe/erpnext/issues/30770)) ([#30771](https://github.com/frappe/erpnext/issues/30771)) ([c339305](c339305e9c))
* Check if accounting dimension exists ([1834671](1834671d59))
* dependent gle reposting (backport [#30726](https://github.com/frappe/erpnext/issues/30726)) ([#30772](https://github.com/frappe/erpnext/issues/30772)) ([a6d0938](a6d0938591))
* Do not validate while creating accounting dimension ([153b41a](153b41a269))
* e_commerce_settings.js ([3a5f5d5](3a5f5d5cd0))
* e_commerce_settings.py ([86c5f4d](86c5f4db85))
* First preference to parent cost center rather than round off cost center ([a2d95fc](a2d95fc62b))
* Handle Multiselect field mapping separately ([dc2f694](dc2f694547))
* **india:** 401 & 403 client error while generating IRN ([198bdcf](198bdcfdc6))
* **india:** cess value not considered while validating e-invoice totals ([#30800](https://github.com/frappe/erpnext/issues/30800)) ([f70fca1](f70fca1c9e))
* **india:** transporter name is null while generating e-way bill ([#30736](https://github.com/frappe/erpnext/issues/30736)) ([6291b28](6291b28c37))
* linter ([6dddbb9](6dddbb9f27))
* Loan doctypes in bank reconciliation ([e69c715](e69c71576d))
* Mistyped variable name in patch ([e76220e](e76220e819))
* monthly attendance sheet ([#30748](https://github.com/frappe/erpnext/issues/30748)) ([0b4e3f1](0b4e3f1467))
* Must not be able to start Job Card if it is related to Work Order that is not started yet ([#29072](https://github.com/frappe/erpnext/issues/29072)) ([#30755](https://github.com/frappe/erpnext/issues/30755)) ([b656ffa](b656ffa45e))
* Query filter fields from Website Item instead of Item master ([bed9e09](bed9e09153))
* select doctype as payment_document ([44f0b69](44f0b69152))
* **Selling,E-Commerce:** Shopping cart quotation without website item. ([#29085](https://github.com/frappe/erpnext/issues/29085)) ([ea0fe5e](ea0fe5e10c))
* SO analysis rpt will fetch SO's without Delivery note as well ([f9d89c7](f9d89c7ce6))
* translation ([#30781](https://github.com/frappe/erpnext/issues/30781)) ([#30783](https://github.com/frappe/erpnext/issues/30783)) ([8335ca6](8335ca6331))
* Update token to allow updates on protected branch ([baab379](baab3797ca))
* update translation ([#30725](https://github.com/frappe/erpnext/issues/30725)) ([#30776](https://github.com/frappe/erpnext/issues/30776)) ([b585262](b585262842))
* Use parent cost center for Sales and Purchase Invoice ([fe9f329](fe9f32946c))
* Use right precision for asset value after full schedule ([#30745](https://github.com/frappe/erpnext/issues/30745)) ([269e192](269e1923c9))
* Validate field filter wrt to Website Item & re-use validation in Item Group ([34437a8](34437a83df))
2022-04-26 07:52:13 +00:00
Deepesh Garg
016839e573 Merge pull request #30805 from frappe/version-13-pre-release
chore: version 13 release
2022-04-26 13:17:29 +05:30
Deepesh Garg
25bc10e0a9 Merge branch 'version-13' into version-13-pre-release 2022-04-26 12:32:06 +05:30
Deepesh Garg
87ec9cd0cc Merge pull request #30804 from frappe/version-13-hotfix
chore: version 13 pre release
2022-04-26 12:25:25 +05:30
Deepesh Garg
2f42cb6362 Merge branch 'version-13-pre-release' into version-13-hotfix 2022-04-26 11:47:34 +05:30
Deepesh Garg
30d1cecf60 Merge pull request #30801 from frappe/mergify/bp/version-13-hotfix/pr-30754
fix: First preference to parent cost center rather than round off cost center (backport #30754)
2022-04-25 17:28:55 +05:30
Deepesh Garg
4522826335 test: Unit test for round off entry dimensions
(cherry picked from commit 3fa1c63479)
2022-04-25 11:37:13 +00:00
Deepesh Garg
1834671d59 fix: Check if accounting dimension exists
(cherry picked from commit c312cd3725)
2022-04-25 11:37:13 +00:00
Deepesh Garg
dedb90ea72 fix: Add accounting dimensions for round off GL Entry
(cherry picked from commit 015812b0b8)
2022-04-25 11:37:12 +00:00
Deepesh Garg
fe9f32946c fix: Use parent cost center for Sales and Purchase Invoice
(cherry picked from commit c42547d40f)
2022-04-25 11:37:12 +00:00
Deepesh Garg
a2d95fc62b fix: First preference to parent cost center rather than round off cost center
(cherry picked from commit 0ac11a5b30)
2022-04-25 11:37:11 +00:00
mergify[bot]
f70fca1c9e fix(india): cess value not considered while validating e-invoice totals (#30800)
(cherry picked from commit 8e6c7a6bf7)

Co-authored-by: Saqib Ansari <nextchamp.saqib@gmail.com>
2022-04-25 15:08:09 +05:30
Deepesh Garg
30bf48e1db Merge pull request #30798 from frappe/mergify/bp/version-13-hotfix/pr-30778
fix: Do not validate while creating accounting dimension (backport #30778)
2022-04-25 14:08:13 +05:30
Deepesh Garg
153b41a269 fix: Do not validate while creating accounting dimension
(cherry picked from commit 9bb132fdd3)
2022-04-25 07:01:28 +00:00
Deepesh Garg
d99cc8b05a Merge pull request #30794 from frappe/mergify/bp/version-13-hotfix/pr-30763
fix: Add loan doctypes in bank clearance (backport #30763)
2022-04-25 08:51:50 +05:30
Deepesh Garg
12f5d65271 test: Remove teardown method
(cherry picked from commit c515abc392)
2022-04-24 15:26:13 +00:00
Deepesh Garg
57485b30b8 test: Fixes in test case
(cherry picked from commit d4d83f4bb6)
2022-04-24 15:26:12 +00:00
Deepesh Garg
1a74c6ee56 test: Fixes in test case
(cherry picked from commit 0eacc99ab7)
2022-04-24 15:26:12 +00:00
Deepesh Garg
728ac1f54e test: Add test coverage for bank clearance
(cherry picked from commit 8a8476bb5c)
2022-04-24 15:26:12 +00:00
Deepesh Garg
44f0b69152 fix: select doctype as payment_document
(cherry picked from commit 3d0e68acaa)
2022-04-24 15:26:11 +00:00
Deepesh Garg
e69c71576d fix: Loan doctypes in bank reconciliation
(cherry picked from commit c3e27b5556)
2022-04-24 15:26:11 +00:00
Deepesh Garg
664a2989a6 Merge pull request #30773 from frappe/mergify/bp/version-13-hotfix/pr-30757
fix(india): 401 & 403 client error while generating IRN (backport #30757)
2022-04-22 19:47:19 +05:30
mergify[bot]
8335ca6331 fix: translation (#30781) (#30783)
fixed the short word for March in german language

(cherry picked from commit 43c1d63ab2)

Co-authored-by: Wolfram Schmidt <wolfram.schmidt@phamos.eu>
2022-04-22 17:39:15 +05:30
Devin Slauenwhite
ea0fe5e10c fix(Selling,E-Commerce): Shopping cart quotation without website item. (#29085)
* test: assert error if quotation contains non website item

* fix(test): validate exception without website item

* fix: shopping cart quotation without website item

* fix: sider issues

* fix: linter trilaing whitespace

* fix: linter format string after translation

* fix: Skip unpublished Variants with published templates in shopping cart quote validation

* style: Re-run pre-commit

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2022-04-22 17:19:29 +05:30
Saqib Ansari
6dddbb9f27 fix: linter 2022-04-22 14:15:21 +05:30
Deepesh Garg
a074d12afd Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-30757 2022-04-22 13:20:09 +05:30
Deepesh Garg
712443a91c chore: Resolve conflicts 2022-04-22 11:48:07 +05:30
mergify[bot]
b585262842 fix: update translation (#30725) (#30776)
* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

(cherry picked from commit e088e65871)

Co-authored-by: Vladislav <integration.into.society@gmail.com>
2022-04-22 11:18:29 +05:30
mergify[bot]
a6d0938591 fix: dependent gle reposting (backport #30726) (#30772)
* refactor: repost error handling

(cherry picked from commit afc5a55a23)

* test: dependent GL entry reposting

(cherry picked from commit a2af2daca7)

# Conflicts:
#	erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py

* fix: dependent GLE reposting

(cherry picked from commit ecdb49314f)

# Conflicts:
#	erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json
#	erpnext/stock/stock_ledger.py

* test: repost queue progress

(cherry picked from commit 8f519545b0)

* test: use disposable item codes in tests

dependency causes flake

(cherry picked from commit d2882ea436)

* fix: correct sorting while updating bin

(cherry picked from commit b24920c0e9)

# Conflicts:
#	erpnext/stock/doctype/bin/bin.py

* fix: sort stock vouchers before reposting GLE

(cherry picked from commit 700e864d90)

* test: discard local future SLE cache between tests

(cherry picked from commit 9734329094)

* chore: conflicts

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-21 19:27:23 +05:30
Saqib Ansari
198bdcfdc6 fix(india): 401 & 403 client error while generating IRN
(cherry picked from commit ee8047aba3)

# Conflicts:
#	erpnext/regional/india/e_invoice/utils.py
2022-04-21 13:26:05 +00:00
Deepesh Garg
32cff94bde chore: Remove conflicts from Sales Order Analysis report (#30761)
* fix: Remove conflicts from Sales Order Analysis report

* fix: change field to duration and fetch elapsed seconds

* chore: Ignore linting check for sql query
2022-04-21 18:31:35 +05:30
mergify[bot]
c339305e9c fix: batch_no filtering not working when batch no is also a number in scientific notation (#30770) (#30771)
[skip ci]

(cherry picked from commit ee3036651a)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-21 16:39:24 +05:30
Marica
ac433dce2a Merge pull request #30179 from MostafaFekry/patch-4
fix: Filter fields in E Commerce Settings (use Website Item)
2022-04-21 14:36:20 +05:30
Marica
d8944b0499 Merge branch 'version-13-hotfix' into patch-4 2022-04-21 12:32:17 +05:30
marination
f58d3b4d3d test: setup e commerce settings before running invalid filtrs test 2022-04-21 12:31:16 +05:30
Ganga Manoj
269e1923c9 fix: Use right precision for asset value after full schedule (#30745) 2022-04-20 19:44:09 +05:30
marination
ba635145da test: Field filter validation and Custom field as field filter
- Test to block Item fields (which aren’t in Website Item) in E Commerce Settings as filters
- Removed unnecessary function and setup in E Commerce Settings test
- Removed commented useless test
- Test to check custom field as filter
2022-04-20 18:54:30 +05:30
marination
dc2f694547 fix: Handle Multiselect field mapping separately
- Map Multiselect child table to Website Item (copy rows)
2022-04-20 17:47:14 +05:30
mergify[bot]
b656ffa45e fix: Must not be able to start Job Card if it is related to Work Order that is not started yet (#29072) (#30755)
* fix: Cannot start Job strat if related to Work Order not started yet

* fix: Cannot start Job strat if related to Work Order not started yet

* test

* test

* fix siders

* PR review

* chore: Code cleanup

- Better short circuit for if condition (make it such that both conditions dont always have to be computed)
- Remove `r.message` extraction by avoiding `then()`

* chore: Remove unnecessary json change

Co-authored-by: marination <maricadsouza221197@gmail.com>
(cherry picked from commit 143786aaa0)

Co-authored-by: HENRY Florian <florian.henry@open-concept.pro>
2022-04-20 15:11:11 +05:30
Raffael Meyer
0b4e3f1467 fix: monthly attendance sheet (#30748) 2022-04-20 12:29:45 +05:30
marination
e76220e819 fix: Mistyped variable name in patch 2022-04-20 12:27:04 +05:30
mergify[bot]
77e8c542dd chore: Update creds to allow updates on protected branch (#30749) (#30750)
(cherry picked from commit e4265ce814)

Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
2022-04-19 17:33:15 +05:30
FinByz Tech Pvt. Ltd
6291b28c37 fix(india): transporter name is null while generating e-way bill (#30736) 2022-04-19 16:26:56 +05:30
semantic-release-bot
4389100fa1 chore(release): Bumped to Version 13.27.0
# [13.27.0](https://github.com/frappe/erpnext/compare/v13.26.0...v13.27.0) (2022-04-19)

### Bug Fixes

* Depreciation Amount calculation for first row when Monthly Depreciation is allowed ([#30692](https://github.com/frappe/erpnext/issues/30692)) ([9cf790d](9cf790db9d))
* Do not show disabled dimensions in reports ([46d773d](46d773df5d))
* Exchange gain and loss on advance jv allocation ([9c5f841](9c5f8415bf))
* Map correct company to PO made via Prod Plan (subcontract) ([44be67e](44be67e6dd))
* Map Production Plan company in subassembly WO created from it ([f604101](f604101fea))
* **patch:** check if column is present while fixing reverse linking (backport [#30737](https://github.com/frappe/erpnext/issues/30737)) ([#30739](https://github.com/frappe/erpnext/issues/30739)) ([38002e3](38002e3fe2))
* Payment reco query with max invocie and payment amount limit ([4008c95](4008c95ac6))
* Price changing on creating Sales retrun from Delivery Note ([93fe840](93fe840844))
* process statement to_date override ([378d15d](378d15d388))
* Remove "Values Out of Sync" validation (backport [#30707](https://github.com/frappe/erpnext/issues/30707)) ([#30718](https://github.com/frappe/erpnext/issues/30718)) ([fb9d640](fb9d640d8b))
* Update token to allow updates on protected branch ([2a29aef](2a29aefb19))
* update translation ([#30716](https://github.com/frappe/erpnext/issues/30716)) ([fb127da](fb127da489))

### Features

* Item-wise provisional accounting for service items ([5776881](5776881f34))
2022-04-19 10:46:00 +00:00
Ankush Menat
f266d765fd chore: add GH token 2022-04-19 15:58:49 +05:30
Deepesh Garg
5915a73939 Merge pull request #30741 from frappe/version-13-pre-release
chore: release for v13.27.0
2022-04-19 15:42:46 +05:30
Deepesh Garg
f6898344ef Merge pull request #30744 from frappe/mergify/bp/version-13-pre-release/pr-30742
fix: Update token to allow updates on protected branch (backport #30742)
2022-04-19 15:33:21 +05:30
Deepesh Garg
2a29aefb19 fix: Update token to allow updates on protected branch
(cherry picked from commit 6f332f3669)
2022-04-19 10:02:33 +00:00
Deepesh Garg
377c37a99f Merge pull request #30743 from frappe/mergify/bp/version-13-hotfix/pr-30742
fix: Update token to allow updates on protected branch (backport #30742)
2022-04-19 15:32:10 +05:30
Deepesh Garg
baab3797ca fix: Update token to allow updates on protected branch
(cherry picked from commit 6f332f3669)
2022-04-19 10:01:58 +00:00
Deepesh Garg
8c291f7b7a Merge pull request #30734 from frappe/mergify/bp/version-13-hotfix/pr-30721
fix: SO's without delivery note will also be fetched (backport #30721)
2022-04-19 14:12:03 +05:30
Deepesh Garg
381e9d2236 Merge pull request #30740 from frappe/version-13-hotfix
chore: Pre-release for v13.27.0
2022-04-19 14:10:58 +05:30
mergify[bot]
38002e3fe2 fix(patch): check if column is present while fixing reverse linking (backport #30737) (#30739)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-04-19 12:39:28 +05:30
marination
9506dbe433 chore: Patch to copy custom fields (field filters) from Item to Website Item 2022-04-18 21:38:22 +05:30
ruthra kumar
61ff1c22b3 test: Sales order analysis report
(cherry picked from commit 13487e2408)
2022-04-18 14:29:06 +00:00
ruthra kumar
f9d89c7ce6 fix: SO analysis rpt will fetch SO's without Delivery note as well
(cherry picked from commit e28e6726f1)

# Conflicts:
#	erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
2022-04-18 14:29:06 +00:00
marination
34437a83df fix: Validate field filter wrt to Website Item & re-use validation in Item Group 2022-04-18 19:24:08 +05:30
marination
bed9e09153 fix: Query filter fields from Website Item instead of Item master
- tweak `filters.py` to correctly query filter field values from Website Item
- Use Website Item for filter field options in Settings and Item Group Field Filter table
2022-04-18 19:23:17 +05:30
Marica
952e267e92 Merge branch 'version-13-hotfix' into patch-4 2022-04-18 18:10:02 +05:30
mergify[bot]
8e30af84cd chore: Add semantic releases (backport #30729) (#30732)
* chore: Add sematic releases

(cherry picked from commit 41249c57c4)

* chore: Update branch name

(cherry picked from commit cc1bdd426b)

* chore: block major releases

(cherry picked from commit c12a36aed9)

* ci: use latest ubuntu container

(cherry picked from commit 6fc11cb4c5)

* chore: do not publish any assets

(cherry picked from commit e0a9a69d76)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
Co-authored-by: Ankush Menat <ankushmenat@gmail.com>
2022-04-18 17:18:53 +05:30
Deepesh Garg
d6126b6695 Merge pull request #30731 from frappe/mergify/bp/version-13-hotfix/pr-30728
fix: Price changing on creating Sales return from Delivery Note (backport #30728)
2022-04-18 16:33:14 +05:30
Deepesh Garg
93fe840844 fix: Price changing on creating Sales retrun from Delivery Note
(cherry picked from commit 9c081947ec)
2022-04-18 09:04:50 +00:00
Deepesh Garg
183ff53ccb Merge pull request #30720 from frappe/mergify/bp/version-13-hotfix/pr-30716
fix: update translation (backport #30716)
2022-04-15 14:01:19 +05:30
HENRY Florian
fb127da489 fix: update translation (#30716)
* fix: update translation

* fix: update translation

* fix: update translation

* fix: update translation

(cherry picked from commit e6aa28ea14)
2022-04-14 16:07:40 +00:00
Deepesh Garg
933bd7413a Merge pull request #30408 from bhavesh95863/patch-3
fix: process statement to_date override
2022-04-14 18:43:28 +05:30
mergify[bot]
fb9d640d8b fix: Remove "Values Out of Sync" validation (backport #30707) (#30718)
This is an automatic backport of pull request #30707 done by [Mergify](https://mergify.com).
Cherry-pick of 89fab78027 has failed:
```
On branch mergify/bp/version-13-hotfix/pr-30707
Your branch is up to date with 'origin/version-13-hotfix'.

You are currently cherry-picking commit 89fab78027.
  (fix conflicts and run "git cherry-pick --continue")
  (use "git cherry-pick --skip" to skip this patch)
  (use "git cherry-pick --abort" to cancel the cherry-pick operation)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
	both modified:   erpnext/accounts/doctype/journal_entry/journal_entry.py

no changes added to commit (use "git add" and/or "git commit -a")
```


To fix up this pull request, you can check it out locally. See documentation: https://docs.github.com/en/github/collaborating-with-pull-requests/reviewing-changes-in-pull-requests/checking-out-pull-requests-locally

---


<details>
<summary>Mergify commands and options</summary>

<br />

More conditions and actions can be found in the [documentation](https://docs.mergify.com/).

You can also trigger Mergify actions by commenting on this pull request:

- `@Mergifyio refresh` will re-evaluate the rules
- `@Mergifyio rebase` will rebase this PR on its base branch
- `@Mergifyio update` will merge the base branch into this PR
- `@Mergifyio backport <destination>` will backport this PR on `<destination>` branch

Additionally, on Mergify [dashboard](https://dashboard.mergify.com/) you can:

- look at your merge queues
- generate the Mergify configuration with the config editor.

Finally, you can contact us on https://mergify.com
</details>
2022-04-14 12:29:35 +00:00
Deepesh Garg
62bad90c90 Merge pull request #30717 from frappe/mergify/bp/version-13-hotfix/pr-30710
fix: Exchange gain and loss on advance Journal Entry allocation (backport #30710)
2022-04-14 15:29:55 +05:30
Deepesh Garg
a167f9e17f test: Update customer in Sales Invoice
(cherry picked from commit c38be53ce8)
2022-04-14 09:40:53 +00:00
Deepesh Garg
e135c452af test: Update customer
(cherry picked from commit 972d9ec5b4)
2022-04-14 09:40:53 +00:00
Deepesh Garg
9c5f8415bf fix: Exchange gain and loss on advance jv allocation
(cherry picked from commit 31883b699d)
2022-04-14 09:40:53 +00:00
Deepesh Garg
46a00d1da6 Merge pull request #30714 from frappe/mergify/bp/version-13-hotfix/pr-30675
feat: Item-wise provisional accounting for service items (backport #30675)
2022-04-14 13:14:48 +05:30
Ankush Menat
3a7ac29907 chore: check in missing __init__.py (#30713) 2022-04-14 13:03:34 +05:30
Deepesh Garg
3dcef9352e test: Update test case
(cherry picked from commit ad171c6225)
2022-04-14 07:01:23 +00:00
Deepesh Garg
5776881f34 feat: Item-wise provisional accounting for service items
(cherry picked from commit 3ce64170db)
2022-04-14 07:01:23 +00:00
Deepesh Garg
0b62784f6f Merge pull request #30700 from deepeshgarg007/pay_reco_query_v13
fix: Payment reco query with max invoice and payment amount limit
2022-04-14 11:11:30 +05:30
Ankush Menat
ef4eb4705e chore: warning for DATEV deprecation (#30703) 2022-04-13 15:07:48 +05:30
Ganga Manoj
9cf790db9d fix: Depreciation Amount calculation for first row when Monthly Depreciation is allowed (#30692) 2022-04-13 14:15:10 +05:30
mergify[bot]
2778123106 Merge pull request #30699 from frappe/mergify/bp/version-13-hotfix/pr-30688
fix: Map Production Plan company in subassembly WO created from it (backport #30688)
2022-04-13 07:30:31 +00:00
marination
44be67e6dd fix: Map correct company to PO made via Prod Plan (subcontract)
(cherry picked from commit 6315acc450)
2022-04-13 07:14:16 +00:00
marination
f604101fea fix: Map Production Plan company in subassembly WO created from it
(cherry picked from commit 2777c5c67c)
2022-04-13 07:14:15 +00:00
Deepesh Garg
4008c95ac6 fix: Payment reco query with max invocie and payment amount limit 2022-04-13 12:21:33 +05:30
Deepesh Garg
512b480f6a chore: version bump 2022-04-12 23:31:47 +05:30
Deepesh Garg
d5d6b34380 Merge pull request #30695 from deepeshgarg007/v13_26_0
chore: change log and version bump
2022-04-12 23:29:44 +05:30
Deepesh Garg
2c2d5ccb8f Merge pull request #30694 from frappe/version-13-pre-release
chore: Release for v13.26.0
2022-04-12 23:28:24 +05:30
Deepesh Garg
147499bc9c chore: change log and version bump 2022-04-12 23:01:09 +05:30
Deepesh Garg
dc669b540d Merge pull request #30691 from frappe/mergify/bp/version-13-hotfix/pr-30689
fix: Do not show disabled dimensions in reports (backport #30689)
2022-04-12 22:28:46 +05:30
Deepesh Garg
08e2b9110c Merge pull request #30693 from frappe/version-13-hotfix
chore: Pre release for v13.26.0
2022-04-12 16:56:52 +05:30
Deepesh Garg
46d773df5d fix: Do not show disabled dimensions in reports
(cherry picked from commit 9a1c560c82)
2022-04-12 10:41:25 +00:00
Deepesh Garg
4a48a6aae3 Merge pull request #30690 from frappe/mergify/bp/version-13-hotfix/pr-30686
feat: Ignore permlevel for specific fields (backport #30686)
2022-04-12 16:10:51 +05:30
Nabin Hait
e650d99cdd feat: Ignore permlevel for specific fields
(cherry picked from commit 993c6c0de9)
2022-04-12 10:26:17 +00:00
Deepesh Garg
23ccadbcae Merge pull request #30683 from frappe/mergify/bp/version-13-hotfix/pr-30651
fix: Download JSON for GSTR-1 report (backport #30651)
2022-04-12 14:00:55 +05:30
Deepesh Garg
bfd8ab6d72 Merge pull request #30687 from frappe/mergify/bp/version-13-hotfix/pr-30626
feat(india): e-invoicing for intra-state union territory transactions (backport #30626)
2022-04-12 14:00:38 +05:30
Saqib Ansari
2ab431ad36 feat(india): e-invoicing for intra-state union territory transactions
(cherry picked from commit 45fca6bed7)
2022-04-12 08:03:23 +00:00
mergify[bot]
21066a48b6 fix: ignore item-less maintenance visit for sr no (#30684) (#30685)
(cherry picked from commit 60fb71bd2a)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-12 11:16:59 +05:30
Deepesh Garg
9efc0d1043 fix: Download JSON for GSTR-1 report
(cherry picked from commit b532ade383)
2022-04-11 18:37:17 +00:00
Deepesh Garg
54ea9319a1 Merge pull request #30680 from frappe/mergify/bp/version-13-hotfix/pr-30602
fix: Deferred Revenue/Expense Account validation (backport #30602)
2022-04-11 20:54:52 +05:30
Marica
f54f9771e4 Merge pull request #30681 from frappe/mergify/bp/version-13-hotfix/pr-30674
fix: Handle multiple item transfer in separate SEs against WO (backport #30674)
2022-04-11 17:46:12 +05:30
marination
e322e7654a test: Multiple RM transfer in separate Stock Entries
- Added test and acceptance of 0 as For Quantity in test helper

(cherry picked from commit 5aa60bb651)
2022-04-11 11:54:48 +00:00
marination
c674180f1a style: Missing Semicolon
(cherry picked from commit be2e5ce966)
2022-04-11 11:54:48 +00:00
marination
d079d8771b fix: Handle multiple item transfer in separate SEs against WO
- Check for pending qty in child items to show/hide "Start" button
- If no qty needed to transfer (FG qty is fulfilled), but RM qty pending: map pending in SE with For Quantity = 0

(cherry picked from commit dfff4beaf4)
2022-04-11 11:54:48 +00:00
Deepesh Garg
4296fc36cc test: Add test
(cherry picked from commit 553178bfe7)
2022-04-11 11:33:33 +00:00
Deepesh Garg
f6c9f052d2 fix: Deferred Revenue/Expense Account validation
(cherry picked from commit 9bf5f76ac8)
2022-04-11 11:33:33 +00:00
Ankush Menat
c9af4e8ce5 chore: broken link 2022-04-11 16:55:22 +05:30
mergify[bot]
e91dea62b8 fix: update translation (#30654) (#30676)
(cherry picked from commit 03c631d723)

Co-authored-by: HENRY Florian <florian.henry@open-concept.pro>
2022-04-11 14:48:17 +05:30
Deepesh Garg
cc3c9c7042 Merge pull request #30662 from deepeshgarg007/implicit_ignore_prcing_rule_check_on_returns
fix: Implicit ignore pricing rule check on returns
2022-04-09 20:15:57 +05:30
Deepesh Garg
8e742d6612 Merge pull request #30663 from frappe/mergify/bp/version-13-hotfix/pr-30542
fix: Ignore disabled tax categories (backport #30542)
2022-04-09 20:15:30 +05:30
Deepesh Garg
37dc11e59a fix: Ignore disabled tax categories
(cherry picked from commit 9a6a181f14)
2022-04-09 14:22:53 +00:00
Deepesh Garg
1b25a7fe76 fix: Implicit ignore pricing rule check on returns 2022-04-09 19:20:51 +05:30
mergify[bot]
e69849d333 fix(pos): cannot change paid amount in pos payments (#30661) 2022-04-09 16:28:25 +05:30
Deepesh Garg
71f72458bf chore: version bump 2022-04-09 16:22:22 +05:30
Saqib Ansari
81812b608f fix(pos): cannot change paid amount in pos payments (#30659) 2022-04-09 16:14:26 +05:30
Saqib Ansari
e4bb81d830 fix(pos): cannot change paid amount in pos payments (#30657) 2022-04-09 16:13:29 +05:30
Jannat Patel
d66979ff52 Merge pull request #30650 from frappe/mergify/bp/version-13-hotfix/pr-30596 2022-04-08 16:50:38 +05:30
Jannat Patel
2ccf58d6ad fix: removed unused courses template
(cherry picked from commit bce1c2a028)
2022-04-08 10:58:18 +00:00
Ankush Menat
9b6c5f2e54 test: prevent cancelling RIV of cancelled voucher
(cherry picked from commit d74181630a)
2022-04-08 16:14:01 +05:30
Ankush Menat
8cac70a63c fix: prevent deleting repost queue for cancelled transactions
(cherry picked from commit a281998bcb)
2022-04-08 16:14:01 +05:30
Deepesh Garg
8d4d12c3b0 Merge pull request #30633 from frappe/mergify/bp/version-13-hotfix/pr-30606
fix: Exchange gain and loss button in Payment Entry (backport #30606)
2022-04-08 13:05:23 +05:30
mergify[bot]
341e8dffd3 fix: remove bad defaults from BOM operation (#30644) (#30645)
[skip ci]

(cherry picked from commit 49560d20bc)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-08 11:27:17 +05:30
Deepesh Garg
13f9207b41 Merge pull request #30620 from nabinhait/party-account-in-ar-ap-report-v13
feat: Receivable/Payable Account column and filter in AR/AP report
2022-04-07 21:24:56 +05:30
Deepesh Garg
a4ea9fa801 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-30606 2022-04-07 21:24:24 +05:30
mergify[bot]
c0cf18d2d6 Merge pull request #30624 from frappe/mergify/bp/version-13-hotfix/pr-30499
feat: 'customer' column and more filter to Payment terms status report (backport #30499)
2022-04-07 13:47:58 +00:00
Ankush Menat
d62928ed65 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-30499 2022-04-07 19:02:47 +05:30
mergify[bot]
4a7ae5c4a7 fix: dont reassign mutable (list) to a different field (backport #30634) (#30635)
This is an automatic backport of pull request #30634 done by [Mergify](https://mergify.com).
Conflicts fixed.
2022-04-07 13:30:24 +00:00
Deepesh Garg
be438c08db fix: Exchange gain and loss button in Payment Entry
(cherry picked from commit 8feb4f08c5)
2022-04-07 11:19:25 +00:00
Nabin Hait
709fcd5051 feat: Receivable/Payable Account column and filter in AR/AP report 2022-04-07 15:56:42 +05:30
mergify[bot]
560c55935f fix: warehouse naming when suffix is present (#30621) (#30629)
(cherry picked from commit be04eaf723)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-07 15:37:33 +05:30
Chillar Anand
30a86951c3 test: Fix failing tests for healthcare service unit (#30625) 2022-04-07 14:58:38 +05:30
mergify[bot]
4b77a4de28 fix: update translation (backport #30474) (#30593)
* fix: update translation (#30474)

* fix: update translation

* fix: update translation

* fix: update translation

* fix: update translation

* fix: update translation

* fix: update translation

* fix: update translation

* fix: update translation

* fix: update translation

* fix: update translation

(cherry picked from commit 4895761d89)

* chore: fix translation csv file

Co-authored-by: HENRY Florian <florian.henry@open-concept.pro>
Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-07 13:24:48 +05:30
Rucha Mahabal
8932d7040f fix: conflicts 2022-04-07 13:21:06 +05:30
Rucha Mahabal
07ff2cb1d3 fix: conflicts 2022-04-07 13:21:06 +05:30
Rucha Mahabal
8393d113d4 fix: show allocation history for earned leaves allocated via scheduler
(cherry picked from commit ec65af5f38)
2022-04-07 13:21:06 +05:30
Rucha Mahabal
c68a38eb5b fix: make New Leaves Allocated read-only if policy assignment is linked to the allocation and leave type is earned leave
(cherry picked from commit 6203ffc8fa)
2022-04-07 13:21:06 +05:30
Rucha Mahabal
0c163eef23 fix: enable Track Changes in Leave Allocation
(cherry picked from commit f8f1c3d8b5)

# Conflicts:
#	erpnext/hr/doctype/leave_allocation/leave_allocation.json
2022-04-07 13:21:06 +05:30
mergify[bot]
af039be03e fix: fallback to item_name if description is not found (backport #30619) (#30622)
* fix: strip html tags before checking for empty description (#30619)

(cherry picked from commit e4c6d6a1a6)

# Conflicts:
#	erpnext/stock/doctype/item/test_item.py

* fix: resolve conflicts

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-07 13:20:36 +05:30
ruthra kumar
39f8ee2ea6 test: added test cases for group filters
(cherry picked from commit 16bfb930f8)
2022-04-07 07:32:04 +00:00
ruthra kumar
a9d5000eab refactor: use group fields from Sales Order and Sales Order Items
(cherry picked from commit b2ed9fd3fe)
2022-04-07 07:32:03 +00:00
ruthra kumar
00acb000dc refactor: item filters are linked with group filters
(cherry picked from commit e324d668d3)
2022-04-07 07:32:02 +00:00
ruthra kumar
9336a3413f refactor: adding new filters and column to test cases
(cherry picked from commit 7558f1b078)
2022-04-07 07:32:02 +00:00
ruthra kumar
c6f946e3e8 feat: additional filters in payment terms status report
(cherry picked from commit eaeadbc422)
2022-04-07 07:32:01 +00:00
Deepesh Garg
da7a2bb3a6 chore: version bump 2022-04-06 22:37:47 +05:30
Saqib Ansari
d720d15e3d fix(pos): reload doc before set value (#30610) 2022-04-06 22:32:23 +05:30
Saqib Ansari
23a7ab15b5 fix(pos): reload doc before set value (#30612) 2022-04-06 22:31:50 +05:30
Saqib Ansari
2e29cf38bb Merge pull request #30611 from nextchamp-saqib/fix-pos-patch-v13
fix(pos): reload doc before set value
2022-04-06 22:28:49 +05:30
Saqib Ansari
f94afc1fff fix(pos): reload doc before set value 2022-04-06 22:14:37 +05:30
Deepesh Garg
78e8f047b5 Merge pull request #30604 from deepeshgarg007/v13_25_0
chore: version bump and change log for v13.25.0
2022-04-06 19:38:41 +05:30
Deepesh Garg
a2d4ae0482 Merge pull request #30605 from frappe/version-13-pre-release
chore: Release for v13.25.0
2022-04-06 19:38:16 +05:30
Deepesh Garg
72d04c66e2 Merge branch 'version-13' into version-13-pre-release 2022-04-06 18:49:58 +05:30
Deepesh Garg
2f7b192ff9 chore: version bump and change log for v13.25.0 2022-04-06 18:46:00 +05:30
Ankush Menat
f89c1b2c0c fix: dont trigger closed WO check on new Job card 2022-04-06 18:28:54 +05:30
mergify[bot]
52aa3561e3 fix: hide pending qty only if original item is assigned (#30599) (#30600)
(cherry picked from commit 8b090a9f7d)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-06 16:38:25 +05:30
mergify[bot]
3c33a5e6ac fix: hide pending qty only if original item is assigned (#30599) (#30601)
(cherry picked from commit 8b090a9f7d)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-06 16:37:15 +05:30
mergify[bot]
14f46a3e26 fix: check null values in is_cancelled patch (#30594) (#30595)
(cherry picked from commit bb875fe217)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-06 15:14:37 +05:30
Deepesh Garg
3fabc48744 Merge pull request #30592 from frappe/version-13-hotfix
chore: Pre-release for v13.25.0
2022-04-06 15:06:22 +05:30
Marica
50e753330f Merge pull request #30520 from frappe/mergify/bp/version-13-hotfix/pr-30146
refactor: Add exception handling in background job within BOM Update Tool (backport #30146)
2022-04-06 13:50:08 +05:30
Deepesh Garg
ff142f8809 Merge pull request #30589 from frappe/mergify/bp/version-13-hotfix/pr-30557
fix: Issues on loan repayment (backport #30557)
2022-04-06 13:38:39 +05:30
Deepesh Garg
d7af8e1dfe fix: Issues on loan repayment
(cherry picked from commit 194605823e)
2022-04-06 06:07:34 +00:00
Deepesh Garg
08bb18ae8a Merge pull request #30586 from frappe/mergify/bp/version-13-hotfix/pr-30555
fix: Ignore user perm for party account company (backport #30555)
2022-04-06 11:35:56 +05:30
Deepesh Garg
ae11562611 test: Ignore parent company account creation
(cherry picked from commit dec0c1b5bb)
2022-04-06 05:27:32 +00:00
Deepesh Garg
027b7e7de1 fix: Ignore user perm for party account company
(cherry picked from commit 18a3c5d536)
2022-04-06 05:27:32 +00:00
Deepesh Garg
3a55cad05f Merge pull request #30575 from deepeshgarg007/pos_shipping
fix: Do not apply shipping rule for POS transactions
2022-04-06 10:00:21 +05:30
Deepesh Garg
95298f0400 fix: Use get instead of dot 2022-04-06 09:27:40 +05:30
marination
e186d76337 fix: Linter 2022-04-05 19:05:30 +05:30
marination
ec646a1a44 fix: User Literal from typing_extensions as its not supported in typing in py 3.7
- https://mypy.readthedocs.io/en/stable/literal_types.html
2022-04-05 18:45:55 +05:30
Marica
c5c578d7b4 Merge pull request #30582 from frappe/mergify/bp/version-13-hotfix/pr-30578
chore: Accessibility for E-commerce Doctypes (backport #30578)
2022-04-05 18:32:02 +05:30
marination
e8f3e23008 fix: Merge Conflicts 2022-04-05 18:22:35 +05:30
marination
c636b366d5 chore: Add Prices, Stock and E-com Settings access from Website Item
(cherry picked from commit 065623ce25)
2022-04-05 12:42:40 +00:00
marination
732b302be0 chore: Accessibility for E-commerce Doctypes
- Add Website Item routing button and dashboard link in Item master
- Group Item variant buttons together

(cherry picked from commit d4301d6d2f)
2022-04-05 12:42:40 +00:00
Saqib Ansari
e5db8c57f9 Merge pull request #30581 from frappe/mergify/bp/version-13-hotfix/pr-30579
fix: fetch from fields not working in eway bill dialog (backport #30579)
2022-04-05 17:07:34 +05:30
Saqib Ansari
072c3d22b1 Merge pull request #30580 from frappe/mergify/bp/version-13-hotfix/pr-30553
fix(india): minor e-invoicing fixes (backport #30553)
2022-04-05 17:07:22 +05:30
Saqib Ansari
0700ee8a06 fix: fetch from fields not working in eway bill dialog
(cherry picked from commit c8779aa446)
2022-04-05 10:22:59 +00:00
Saqib Ansari
e1bd156f40 fix: server error while viewing gst e-invoice
(cherry picked from commit b91bf40f1b)
2022-04-05 10:22:23 +00:00
Saqib Ansari
01404b2b5b fix(india): cannot generate e-invoice for is_pos invoices
* If mode of payment > 18 characters, the e-invoice portal throws error

(cherry picked from commit 0c26f9a8c8)
2022-04-05 10:22:23 +00:00
Rucha Mahabal
6b1a27a641 Merge pull request #30576 from frappe/mergify/bp/version-13-hotfix/pr-30569 2022-04-04 20:40:49 +05:30
Rucha Mahabal
cb2a8aab31 fix(test): set company for employee in leave allocation test setup
(cherry picked from commit 793164ac2e)
2022-04-04 14:49:20 +00:00
Rucha Mahabal
e41f35aa82 test: leave allocation validations and total value for updates done before and after submission
(cherry picked from commit 5499cecffd)
2022-04-04 14:49:20 +00:00
Rucha Mahabal
1093307dcc fix: total leaves allocated not validated and recalculated on updates post submission
(cherry picked from commit 3538656a7d)
2022-04-04 14:49:19 +00:00
rohitwaghchaure
3f13ce5620 Merge pull request #30570 from frappe/mergify/bp/version-13-hotfix/pr-30564
fix: if accepted warehouse not selected during rejection then stock ledger not created (backport #30564)
2022-04-04 20:15:15 +05:30
Deepesh Garg
c0ebcfb393 fix: Do not apply shipping rule for POS transactions 2022-04-04 20:05:10 +05:30
Marica
761f8e0b1c Merge pull request #30539 from marination/redisearch-app-install-v13
feat: Redisearch with consent (bp)
2022-04-04 19:25:29 +05:30
Sherin KR
0a2c72c594 fix: Validation for single threshold in Tax With Holding Category (#30382) 2022-04-04 19:07:51 +05:30
Rohit Waghchaure
3038a5cd5a test: test case to validate rejected qty without accepted warehouse
(cherry picked from commit ac5df1abbe)
2022-04-04 12:57:04 +00:00
Rohit Waghchaure
157461ed02 fix: if accepted warehouse not selected during rejection then stock ledger not created
(cherry picked from commit 0a71cabab1)
2022-04-04 12:57:04 +00:00
Marica
3e84607cd4 Merge branch 'version-13-hotfix' into redisearch-app-install-v13 2022-04-04 18:01:36 +05:30
mergify[bot]
012cab80bf fix(ux): refresh update to zero val checkbox (#30567) (#30568)
(cherry picked from commit de83511091)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-04 17:31:26 +05:30
mergify[bot]
b524e657e2 fix(pos): do not reset search input on item selection (backport #30537) 2022-04-04 16:35:46 +05:30
Ankush Menat
4cc23830f1 fix: maintain FIFO queue even if outgoing_rate is not found (#30563)
port of #30560
2022-04-04 15:46:49 +05:30
Marica
e5c303e9f4 Merge branch 'version-13-hotfix' into redisearch-app-install-v13 2022-04-04 13:28:09 +05:30
marination
4dc7047a59 chore: Add TODOs(perf) for Redisearch Query 2022-04-04 13:26:08 +05:30
marination
42ec9db5f4 fix: Payload incorrect data (pass item_group.name) 2022-04-04 12:35:14 +05:30
marination
0f9277a8c8 fix: Add default score of 1 to Item Group Autocompleter
- If score 0 is inserted into suggestions, RS does not consider that suggestion
2022-04-04 12:06:27 +05:30
marination
5c36d12369 fix: Convert payload to string before adding to autocompleter 2022-04-04 11:34:39 +05:30
marination
b4d427b429 fix: Better Exception Handling and vaeiabl naming
- Function to handle RS exceptions (create log and raise error)
- Handle `ResponseError` where it is anticipated
- Misc: Better variables
2022-04-04 11:15:45 +05:30
rohitwaghchaure
d032a0cd77 Merge pull request #30450 from anupamvs/crm-contract-naming-v13
feat: configurable Contract naming
2022-04-02 23:43:02 +05:30
Deepesh Garg
00f6565596 Merge pull request #30550 from frappe/mergify/bp/version-13-hotfix/pr-30191
fix: incorrect payable amount for loan closure (backport #30191)
2022-04-02 23:32:56 +05:30
Deepesh Garg
7bf6de1883 fix: Resolve conflicts 2022-04-02 20:33:46 +05:30
Deepesh Garg
3fc43cb259 fix: Code cleanup
(cherry picked from commit 1b2c6a5b78)

# Conflicts:
#	erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
2022-04-02 14:23:50 +00:00
Abhinav Raut
1d9a6efb1b fix: incorrect payable amount for loan closure
(cherry picked from commit 8c76a76154)

# Conflicts:
#	erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
2022-04-02 14:23:50 +00:00
Abhinav Raut
c44a4e559b fix: incorrect payable amount for loan closure
- Add penalty amount to payable amount for loan closure

(cherry picked from commit 4e92926a52)

# Conflicts:
#	erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
2022-04-02 14:23:49 +00:00
marination
17f95b1f83 fix: Use Payload in AutoCompleter (categories in search) and misc
- Separate Item group and Item autocomplete dict definition
- Add payload along with Item group, containing namke and route
- Pass weightage while defining item group autocomplete dict (auto sort)
- Use payload while getting results for categories in search
- Remove check to show categories, always show
- Search fields mandatory if reidsearch enabled
- Code separation (rough)
2022-04-01 18:51:22 +05:30
mergify[bot]
c5fd77ebf7 perf: index barcode for faster scans (#30543) (#30544)
(cherry picked from commit 6c5b01c60d)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-04-01 15:50:04 +05:30
Alberto826
63dacaa03d fix: Remove trailing slashes "/" from route (#30531)
Trailing slashes "/" in the routes causes redirect to port 8080 in docker implementation with reverse proxy.
The "student_admission_row.html" template has an href attribute with a trailing slash.
2022-04-01 14:48:15 +05:30
mergify[bot]
a35cc7004d fix: convert dates to datetime before comparing in leave days calculation and fix half day edge case (backport #30538) (#30541) 2022-04-01 14:17:25 +05:30
marination
fd29722d36 fix: Call Redisearch index creation functions on enabling redisearch in settings 2022-04-01 13:05:17 +05:30
Saqib Ansari
9a5acc313c Merge pull request #30515 from frappe/mergify/copy/version-13-hotfix/pr-30324
fix: multiple pos issues (copy #30324)
2022-04-01 12:11:06 +05:30
Saqib Ansari
9335578a0b fix: linting errors 2022-04-01 11:02:18 +05:30
Deepesh Garg
af6dcd66dc Merge pull request #30534 from frappe/mergify/bp/version-13-hotfix/pr-30510
fix: Taxes getting overriden from mapped to target doc (backport #30510)
2022-04-01 10:31:32 +05:30
Deepesh Garg
6c53ca6328 Merge pull request #30512 from frappe/mergify/bp/version-13-hotfix/pr-30486
fix: Account currency validation (backport #30486)
2022-04-01 09:45:45 +05:30
Deepesh Garg
20df023b9a Merge pull request #30525 from frappe/mergify/bp/version-13-hotfix/pr-30500
fix(India): Tax fetching based on tax category (backport #30500)
2022-04-01 09:45:28 +05:30
Deepesh Garg
328b9431b2 fix: Taxes getting overriden from mapped to target doc
(cherry picked from commit 4720969ce6)
2022-04-01 04:14:07 +00:00
Saqib Ansari
a9f24859ad Merge pull request #30514 from frappe/mergify/bp/version-13-hotfix/pr-30461
fix(asset): do not validate warehouse on asset purchase (backport #30461)
2022-03-31 21:08:51 +05:30
rohitwaghchaure
dbb872aeb4 Merge pull request #30530 from frappe/mergify/bp/version-13-hotfix/pr-30527
feat: minor, pick list item reference on delivery note item table (backport #30527)
2022-03-31 21:04:02 +05:30
Rohit Waghchaure
a74198f974 test: test case to check pick list name has mapped or not
(cherry picked from commit 2f51011f91)
2022-03-31 14:53:52 +00:00
Rohit Waghchaure
c92df4eed3 feat: minor, pick list item reference on delivery note item table
(cherry picked from commit 2f63ae2ee9)
2022-03-31 14:53:51 +00:00
Deepesh Garg
f213dc9999 fix(India): Tax fetching based on tax category
(cherry picked from commit 532961fad2)
2022-03-31 12:32:41 +00:00
Saqib Ansari
8416dc713c fix: unexpected keyword argument 'pluck' 2022-03-31 16:32:01 +05:30
Saqib Ansari
8d315a6573 fix: 'int' object has no attribute 'is_draft' 2022-03-31 16:17:51 +05:30
marination
a9ec72d833 chore: Added BOM std filters and update type in List View
(cherry picked from commit 2fece523f6)
2022-03-31 10:25:51 +00:00
marination
770f8da792 test: Added test for 2 more validations
- Covers full validate function

(cherry picked from commit a945484af4)
2022-03-31 10:25:51 +00:00
marination
0d3c8e4d74 fix: Type Annotations, Redundancy, etc.
- Renamed public function`update_new_bom` to `update_new_bom_in_bom_items`
- Replaced `get_cached_doc` with `get_doc`
- Removed click progress bar (drive through update log)
- Removed `bom_obj.update_new_bom()`, was redundant. Did same job as `update_new_bom_in_bom_items`
- Removed `update_new_bom()` in `bom.py`, unused.
- Prettier query formatting
- `update_type` annotated as non optional Literal
- Removed redundant use of JobTimeoutException
- Corrected type annotations in `create_bom_update_log()`

(cherry picked from commit 620575a901)

# Conflicts:
#	erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
2022-03-31 10:25:50 +00:00
marination
c0c39f8c79 fix: Semgrep
- Explain explicit commits and skip semgrep
- Format client side translated string correctly

(cherry picked from commit ebf00946c9)
2022-03-31 10:25:48 +00:00
marination
1f9ecb3397 fix: Auto format bom_update_log.py
(cherry picked from commit 79495679e2)
2022-03-31 10:25:47 +00:00
marination
9b069ed04b test: API hit via BOM Update Tool
- test creation of log and it's impact

(cherry picked from commit 1d1e925bcf)
2022-03-31 10:25:47 +00:00
Marica
70485a6afc Merge pull request #30517 from frappe/mergify/bp/version-13-hotfix/pr-30509
fix: Add non-existent Item check and cleanup in `validate_for_items` (backport #30509)
2022-03-31 15:55:46 +05:30
marination
5dca5563ff fix: Test, Sider and Added button to access log from Tool
(cherry picked from commit f3715ab382)

# Conflicts:
#	erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
2022-03-31 10:25:45 +00:00
marination
8b5e759965 fix: Sider and Linter
(cherry picked from commit 3e3af95712)
2022-03-31 10:25:43 +00:00
marination
444af4588f feat: List View indicators for Log and Error Log link in log
(cherry picked from commit 8aff75f8e8)
2022-03-31 10:25:42 +00:00
marination
59af556241 chore: Polish error handling and code sepration
- Added Typing
- Moved all job business logic to bom update log
- Added `run_bom_job` that handles errors and runs either of two methods
- UX: Replace button disabled until both inputs are filled
- Show log creation message on UI for correctness
- APIs return log document as result
- Converted raw sql to QB

(cherry picked from commit cff91558d4)

# Conflicts:
#	erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
2022-03-31 10:25:41 +00:00
marination
7aa37ec511 feat: BOM Update Log
- Created BOM Update Log that will handle queued job status and failures
- Moved validation and BG job to thus new doctype
- BOM Update Tool only works as an endpoint

(cherry picked from commit 4283a13e5a)

# Conflicts:
#	erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py
2022-03-31 10:25:39 +00:00
marination
7317a0696b refactor: Add exception handling in background job within BOM Update Tool
(cherry picked from commit f57725f8fa)
2022-03-31 10:25:37 +00:00
marination
40a154e64d fix: (test) change expected exception due to https://github.com/frappe/frappe/pull/16454
(cherry picked from commit 93f6346cea)
2022-03-31 09:23:47 +00:00
marination
84247e91f3 fix: Add non-existent Item check and cleanup in validate_for_items
- Added a validation if invalid item code ia passed via data import/API, etc.
- Used DB APIs instead of raw sql
- Separated checks into separate functions
- Added return types to functions
- Better variable naming and removed redundant utils import in function

(cherry picked from commit 982a246eec)
2022-03-31 09:23:46 +00:00
Saqib Ansari
ab7417c26a fix: invalid keyword argument 'pluck' 2022-03-31 14:38:40 +05:30
Saqib Ansari
0bafec2384 fix: merge conflicts 2022-03-31 14:24:42 +05:30
Saqib Ansari
a51b32b7e0 fix: merge conflicts 2022-03-31 14:19:53 +05:30
Saqib Ansari
47567c66c1 chore: ignore rules for QB formatting
(cherry picked from commit e0c36d87e0)
2022-03-31 08:47:20 +00:00
Saqib Ansari
82aea2b998 fix: set is_return & return_against in POS Invoice Reference table
(cherry picked from commit 16253a2f72)

# Conflicts:
#	erpnext/patches.txt
2022-03-31 08:47:20 +00:00
Saqib Ansari
cf3e09588f fix: test cases
(cherry picked from commit 1b556d1c53)
2022-03-31 08:47:18 +00:00
Saqib Ansari
650274e973 fix: sider issues
(cherry picked from commit cb4873c019)
2022-03-31 08:47:17 +00:00
Saqib Ansari
3bb0716dff fix(pos): cannot close the pos if sr. no. is sold & returned
(cherry picked from commit cf51a0a1b8)

# Conflicts:
#	erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
#	erpnext/accounts/doctype/pos_invoice_merge_log/test_pos_invoice_merge_log.py
2022-03-31 08:47:17 +00:00
Saqib Ansari
36845a87e0 fix(pos): remove returned sr. nos. from pos reserved sr. nos. list
(cherry picked from commit f2ae63cbfd)

# Conflicts:
#	erpnext/accounts/doctype/pos_invoice/pos_invoice.py
#	erpnext/stock/doctype/serial_no/serial_no.py
2022-03-31 08:47:15 +00:00
Saqib Ansari
76f83ea559 fix(pos): allow validating stock on save
(cherry picked from commit aff7408775)

# Conflicts:
#	erpnext/accounts/doctype/pos_invoice/pos_invoice.py
2022-03-31 08:47:13 +00:00
Saqib Ansari
3b583c6c48 fix(pos): specific case when serialized item not removed
(cherry picked from commit 4afb47e869)
2022-03-31 08:47:11 +00:00
Saqib Ansari
a3a7dc9ce6 fix(pos): customer group filter in customer selector
(cherry picked from commit 2f82e237ef)
2022-03-31 08:47:10 +00:00
Saqib Ansari
bd2061d6f3 fix: prevent multiple save on applying coupon code
(cherry picked from commit d5fd8e0ba6)
2022-03-31 08:47:09 +00:00
Saqib Ansari
b4a10d571f fix(test): Item MacBook does not exist
(cherry picked from commit 4623a1bc57)
2022-03-31 08:46:17 +00:00
Saqib Ansari
c36b5d9ab8 perf: skip warehouse validation for non-stock items
(cherry picked from commit 199a6da960)

# Conflicts:
#	erpnext/controllers/accounts_controller.py
2022-03-31 08:46:16 +00:00
Saqib Ansari
ad91d57a41 perf: skip warehouse validation for non-stock items
(cherry picked from commit 6528218ac3)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
2022-03-31 08:46:15 +00:00
Saqib Ansari
00cb0d0294 fix(asset): do not validate warehouse on asset purchase
(cherry picked from commit 136466d255)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
2022-03-31 08:46:13 +00:00
Deepesh Garg
ebb2e975cd fix: make test record
(cherry picked from commit d93edbc859)
2022-03-31 08:10:25 +00:00
Deepesh Garg
168cc353ca fix: Account currency validation
(cherry picked from commit d4cc7c553e)
2022-03-31 08:10:25 +00:00
Anupam
444625a0ca fix: linter issues 2022-03-31 13:38:37 +05:30
Saqib Ansari
9ffe34757e Merge pull request #30455 from frappe/mergify/bp/version-13-hotfix/pr-30419
fix: move item tax to item tax template patch (backport #30419)
2022-03-31 12:43:15 +05:30
Anupam
af2d55f893 Merge branch 'crm-contract-naming-v13' of github.com:anupamvs/erpnext into crm-contract-naming-v13 2022-03-31 12:05:46 +05:30
Anupam
20ef6ab5bf fix: review changes 2022-03-31 12:04:53 +05:30
Saqib Ansari
1a169c9dc5 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-30419 2022-03-31 12:02:15 +05:30
mergify[bot]
22ec1a4996 fix: enable row deletion in reference table (#30492)
(cherry picked from commit 500870b2b0)

Co-authored-by: rahib-hassan <rahib.hassan10@gmail.com>
2022-03-30 16:37:00 +05:30
Anoop
3f3717952c fix: cast array slice index integer while splitting serial_nos array (#30468) 2022-03-30 16:36:15 +05:30
mergify[bot]
c7d8d60de6 fix: explicitly check if additional salary is recurring while fetching components for payroll (backport #30489) (#30491)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-03-30 15:07:56 +05:30
Saqib Ansari
6397456ba6 Merge pull request #30484 from frappe/mergify/bp/version-13-hotfix/pr-30470
fix: credit limit validation in delivery note (backport #30470)
2022-03-30 14:57:49 +05:30
mergify[bot]
46e6d16c49 fix: Dont set idx while adding WO items to Stock Entry (backport #30377) (#30485)
* fix: Dont set `idx` while adding WO items to Stock Entry

- `idx` must be computed by base document's `self.append()` function, so do not set it

(cherry picked from commit a787ebb732)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/stock_entry.py

* chore: Remove redundant idx query and value setting

- idx can be removed from `select_columns` as it is already in the main query
- setting idx to '' is not required as it is not used further

(cherry picked from commit 639d380c1f)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/stock_entry.py

* test: idx mapping correctness

(cherry picked from commit fa3b953cf7)

* fix: Linter

(cherry picked from commit b5ad626d23)

* fix: resolve conflicts

Co-authored-by: marination <maricadsouza221197@gmail.com>
Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-30 12:05:26 +05:30
Saqib Ansari
c9cd3ee9aa fix: credit limit validation in delivery note
(cherry picked from commit c122882884)
2022-03-30 05:39:15 +00:00
Ankush Menat
7a2729e5bb Merge branch 'version-13-hotfix' into version-13-pre-release 2022-03-29 18:41:22 +05:30
mergify[bot]
39ff7b0b06 fix: validate 0 transfer qty in stock entry (#30476) (#30479)
(cherry picked from commit b80fac03af)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-29 18:18:13 +05:30
Ankush Menat
eedfee367c Merge pull request #30478 from ankush/format_v13
style: bulk formatting (v13 port)
2022-03-29 17:51:30 +05:30
Ankush Menat
14c8386040 chore: ignore black formatting commit in blame 2022-03-29 17:31:43 +05:30
Ankush Menat
c07713b860 style: bulk format code with black
v13 port because otherwise backports will result in conflicts always
2022-03-29 17:29:34 +05:30
Ankush Menat
7cc84dcbb4 ci: pin click for black (#30464)
[skip ci]
2022-03-29 17:26:42 +05:30
Ankush Menat
68ded18a23 ci: force black formatting (pre-commit) 2022-03-29 17:26:26 +05:30
mergify[bot]
00ad515ac7 fix: use name for links not item_code (backport #30462) (#30463)
* fix: use `name` for links not `item_code` (#30462)

(cherry picked from commit 76dce2eddc)

# Conflicts:
#	erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.py

* fix: conflicts

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-29 17:16:31 +05:30
Deepesh Garg
751a6354f1 Merge pull request #30471 from deepeshgarg007/release_v13_24_0
chore: version bump an change log for v13.24.0
2022-03-29 14:35:21 +05:30
Deepesh Garg
33071a4731 Merge pull request #30469 from frappe/version-13-pre-release
chore: v13.24.0 release
2022-03-29 14:29:09 +05:30
Deepesh Garg
e1a0f067b0 chore: version bump an change log for v13.24.0 2022-03-29 14:18:37 +05:30
mergify[bot]
6b3bdb2e9e fix: dont check for failed repost while freezing (backport #30472) (#30473)
* fix: dont check for failed repost while freezing (#30472)

[skip ci]

(cherry picked from commit b12fe0f15b)

# Conflicts:
#	erpnext/stock/utils.py

* fix: conflicts

[skip ci]

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-29 13:56:15 +05:30
Deepesh Garg
a7b2fa31a1 chore: version bump an change log for v13.24.0 2022-03-29 13:38:39 +05:30
Deepesh Garg
4ee08e390a Merge pull request #30460 from frappe/mergify/bp/version-13-pre-release/pr-30385
fix(India): Auto tax fetching based on GSTIN (backport #30385)
2022-03-29 12:32:10 +05:30
Anupam Kumar
825fea0883 Merge branch 'version-13-hotfix' into crm-contract-naming-v13 2022-03-29 12:11:57 +05:30
Deepesh Garg
504f213742 fix: Resolve conflicts 2022-03-29 10:35:44 +05:30
Deepesh Garg
3ff77f9cb3 fix(India): Auto tax fetching based on GSTIN
(cherry picked from commit 7cae669e81)

# Conflicts:
#	erpnext/patches.txt
2022-03-29 04:57:17 +00:00
Deepesh Garg
8e59e67197 Merge pull request #30385 from deepeshgarg007/quotation_gst
fix(India): Auto tax fetching based on GSTIN
2022-03-29 10:25:57 +05:30
mergify[bot]
659fa7a190 fix: bom valuation - handle lack of LPP (#30454) (#30457)
(cherry picked from commit 8dff4d66a4)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-28 18:36:12 +05:30
Mostafa Fekry
86c5f4db85 fix: e_commerce_settings.py
Update get meta from Item to Website Item doctype to check validate_field_filters
2022-03-28 18:15:46 +05:30
Ankush Menat
fc5fd34621 Merge branch 'version-13-hotfix' into patch-4 2022-03-28 18:06:50 +05:30
Raffael Meyer
471a3279b9 fix(lead): reload address and contact before updating their links (Backport #29966) (#29968)
* fix(lead): reload address and contact before updating their links

They might have changed since they were created. Backport of #29966.

* refactor: reload contact after insert
2022-03-28 18:01:00 +05:30
Saqib Ansari
3142e6f188 fix: move item tax to item tax template patch
(cherry picked from commit 936267c934)
2022-03-28 12:05:23 +00:00
Deepesh Garg
ea2b0e224e Merge pull request #30451 from frappe/version-13-hotfix
chore: Pre release for v13.24.0
2022-03-28 16:59:18 +05:30
Anupam
1efb317243 feat: configurable Contract naming 2022-03-28 14:36:43 +05:30
Ankush Menat
7f3651a938 fix: update picked qty on cancellation
(cherry picked from commit 69ae2661d2)
2022-03-28 10:44:53 +05:30
Deepesh Garg
8fb56d57bb Merge pull request #30438 from deepeshgarg007/amount_to_pay
fix: Incorrect default amount to pay for POS invoices
2022-03-27 18:00:42 +05:30
Deepesh Garg
156416bcc3 Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into quotation_gst 2022-03-27 17:28:42 +05:30
Deepesh Garg
a044e92687 fix: Incorrect default amount to pay for POS invoices 2022-03-27 13:02:31 +05:30
Saqib Ansari
99dc1ecd2d Merge pull request #30435 from frappe/mergify/bp/version-13-hotfix/pr-30420
fix: unsupported operand type(s) for +=: 'int' and 'NoneType' (backport #30420)
2022-03-26 20:00:07 +05:30
Saqib Ansari
bd171fbd42 fix: unsupported operand type(s) for +=: 'int' and 'NoneType'
(cherry picked from commit 5450542fdb)
2022-03-26 13:46:24 +00:00
Rucha Mahabal
020a4269ae fix(Expense Claim): validate advances after setting totals 2022-03-25 23:43:46 +05:30
Rucha Mahabal
84de986c5a fix(test): add function for getting advances for claim 2022-03-25 23:43:46 +05:30
Rucha Mahabal
e783e3f93e fix: do not update status to Paid if sanctioned amount is 0 2022-03-25 23:43:46 +05:30
Rucha Mahabal
ed0a685e0e chore: sort imports 2022-03-25 23:43:46 +05:30
Rucha Mahabal
7d3c211b1f test: expense claim against fully and partially paid advances 2022-03-25 23:43:46 +05:30
Rucha Mahabal
961e691bdc fix: Expense Claim conditions for Paid status
- status doesn't change to Paid if the entire required amount is already covered via linked advances

- status doesn't change to Paid if the claim is partially paid via advances

- patch to update such uncancelled claims to Paid
2022-03-25 23:43:46 +05:30
Deepesh Garg
5308bb05fc chore: version bump 2022-03-25 21:57:48 +05:30
Deepesh Garg
985e3007e7 Merge pull request #30432 from frappe/mergify/bp/version-13/pr-30429
fix: Check for onload property (backport #30429)
2022-03-25 21:55:47 +05:30
Deepesh Garg
d6703cef55 Merge pull request #30431 from frappe/mergify/bp/version-13-pre-release/pr-30429
fix: Check for onload property (backport #30429)
2022-03-25 21:55:20 +05:30
Deepesh Garg
430c9c9df4 fix: Check for onload property
(cherry picked from commit 71402b43a7)
2022-03-25 16:24:24 +00:00
Deepesh Garg
071064c097 fix: Check for onload property
(cherry picked from commit 71402b43a7)
2022-03-25 16:24:02 +00:00
Deepesh Garg
c2b2b3962a Merge pull request #30429 from deepeshgarg007/transaction_issue
fix: Check for onload property
2022-03-25 21:51:07 +05:30
Deepesh Garg
71402b43a7 fix: Check for onload property 2022-03-25 21:49:19 +05:30
Deepesh Garg
0740158431 Merge pull request #30395 from deepeshgarg007/pos_write_off
fix: Write off amount wrongly calculated in POS Invoice
2022-03-25 20:56:19 +05:30
mergify[bot]
8b5fe164fd fix: show subassembly table always (#30422) (#30423)
[skip ci]

(cherry picked from commit 0534cf6c9c)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-25 19:04:22 +05:30
Deepesh Garg
6a50f36b31 test: test for auto write-off amount 2022-03-25 18:02:14 +05:30
Deepesh Garg
665bd4c510 Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into pos_write_off 2022-03-25 16:00:33 +05:30
Deepesh Garg
04a25b0e19 chore: version bump 2022-03-25 13:07:45 +05:30
Deepesh Garg
c2911998a1 Merge pull request #30415 from frappe/mergify/bp/version-13/pr-30406
fix: Rate change issue on save and mapping from other doc (backport #30406)
2022-03-25 13:02:17 +05:30
Deepesh Garg
73d4031100 Merge pull request #30414 from frappe/mergify/bp/version-13-pre-release/pr-30406
fix: Rate change issue on save and mapping from other doc (backport #30406)
2022-03-25 13:02:01 +05:30
Deepesh Garg
f892eb588a fix: Condition
(cherry picked from commit 2e0e6ca6b1)
2022-03-25 07:31:37 +00:00
Deepesh Garg
858fd6fce3 fix: Revert rate calculation
(cherry picked from commit 6937a498e7)
2022-03-25 07:31:36 +00:00
Deepesh Garg
f40c5cb681 fix: Add back calculation for discount
(cherry picked from commit 067564bd26)
2022-03-25 07:31:36 +00:00
Deepesh Garg
8ff47999ab fix: Rate change issue on save and mapping from other doc
(cherry picked from commit 13fcda5776)
2022-03-25 07:31:36 +00:00
Deepesh Garg
def18f2b13 fix: Condition
(cherry picked from commit 2e0e6ca6b1)
2022-03-25 07:31:08 +00:00
Deepesh Garg
6c23dea716 fix: Revert rate calculation
(cherry picked from commit 6937a498e7)
2022-03-25 07:31:08 +00:00
Deepesh Garg
4737cb10f3 fix: Add back calculation for discount
(cherry picked from commit 067564bd26)
2022-03-25 07:31:07 +00:00
Deepesh Garg
13e73f6a38 fix: Rate change issue on save and mapping from other doc
(cherry picked from commit 13fcda5776)
2022-03-25 07:31:07 +00:00
Deepesh Garg
26b90ffce0 Merge pull request #30406 from deepeshgarg007/rate_change_fixes
fix: Rate change issue on save and mapping from other doc
2022-03-25 12:59:40 +05:30
mergify[bot]
29fde6ee8b fix: failing broken patches (#30409) (#30412)
* fix: failing broken patches

* refactor: simpler conditions

[skip ci]

(cherry picked from commit c4854bb1b1)

Co-authored-by: Shadrak Gurupnor <30501401+shadrak98@users.noreply.github.com>
2022-03-25 12:57:05 +05:30
Deepesh Garg
2e0e6ca6b1 fix: Condition 2022-03-25 12:39:59 +05:30
Deepesh Garg
6937a498e7 fix: Revert rate calculation 2022-03-25 12:28:55 +05:30
Deepesh Garg
067564bd26 fix: Add back calculation for discount 2022-03-25 12:17:51 +05:30
Bhavesh Maheshwari
378d15d388 fix: process statement to_date override 2022-03-25 11:27:19 +05:30
Deepesh Garg
13fcda5776 fix: Rate change issue on save and mapping from other doc 2022-03-25 10:46:09 +05:30
Ankush Menat
20794ac9ce fix: consider all existing PO items
When this is sent from API/client side doesn't send temporary_name it
can be flaky. Hence, use all available names for validation.
2022-03-24 18:00:00 +05:30
mergify[bot]
03d7fc1570 fix: broken production item links on production plan (backport #30399) (#30400)
* fix: only validate qty for main non-subassy items

(cherry picked from commit 3d43c437ad)

* fix: subassembly items linked to temporary name

Production Plan tables for po_items and sub_assembly_items are prepared
client side so both dont exist at time of first save or modifying and
hence any "links" created are invalid. This change retains temporary
name so it can be relinked server side after naming is performed.

Co-Authored-By: Marica <maricadsouza221197@gmail.com>
(cherry picked from commit 5b1d6055e6)

* test: dont resubmit WO

[skip ci]

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-24 17:30:58 +05:30
Deepesh Garg
d3fd2fd2c5 fix: Ignore for Purchase Invoices 2022-03-24 13:35:32 +05:30
Deepesh Garg
ed38679d22 fix: Ignore for Purchase Invoices 2022-03-24 13:09:55 +05:30
mergify[bot]
26fe2388e1 fix: (ux) Add is_group=0 filter on website warehouse (#30396) (#30397)
- It does not support group warehouses right now and it is misleading

(cherry picked from commit d24458ab77)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2022-03-24 13:01:06 +05:30
Deepesh Garg
2e33e748ea fix: Client side changes for POS Write off amount 2022-03-24 12:56:22 +05:30
Deepesh Garg
ee4258e42c fix: Write off amount wrongly calculated in POS Invoice 2022-03-24 12:31:37 +05:30
Deepesh Garg
e63f20b3f7 Merge pull request #30389 from frappe/mergify/bp/version-13-hotfix/pr-30334
fix: Reset GST State number (backport #30334)
2022-03-24 10:11:13 +05:30
Deepesh Garg
cd20502c7e fix: Reset GST State number
(cherry picked from commit 363e676a35)
2022-03-23 18:00:47 +00:00
Deepesh Garg
aded1fca3e Merge pull request #30343 from deepeshgarg007/item_tax_template_bug
fix: Taxes not getting fetched from item tax template
2022-03-23 22:02:17 +05:30
Deepesh Garg
7cae669e81 fix(India): Auto tax fetching based on GSTIN 2022-03-23 18:47:58 +05:30
Ankush Menat
54e4ff19ee fix: make auto created job cards "Open"
(cherry picked from commit 6418dc1606)
2022-03-23 16:46:22 +05:30
Ankush Menat
ff38d0498b fix: material transfer status should be before WIP
(cherry picked from commit e1f5620654)
2022-03-23 16:46:22 +05:30
Ankush Menat
a1740b0762 fix: ignore items without info for pending qty computation
(cherry picked from commit 4aa74af90d)
2022-03-23 15:51:29 +05:30
Ankush Menat
277c03e157 chore(DX): add sourceURL for debugging
(cherry picked from commit be16fb9dbb)
2022-03-23 15:51:29 +05:30
Deepesh Garg
67b9f43330 Merge pull request #30367 from frappe/mergify/bp/version-13-hotfix/pr-30365
fix: Changing item prices on converting orders/receipts to invoices (backport #30365)
2022-03-23 14:27:22 +05:30
Deepesh Garg
a5b29c1722 Merge pull request #30373 from frappe/mergify/bp/version-13/pr-30368
fix: Changing item prices on converting orders/receipts to invoices (backport #30365) (backport #30368)
2022-03-23 14:22:16 +05:30
Deepesh Garg
14ac2d39ab fix: Check if onload property exists in the form object
(cherry picked from commit 41b2d98c3f)
2022-03-23 08:48:17 +00:00
Deepesh Garg
740a9da1ee fix: Add ignore pricelist for missing flows
(cherry picked from commit 405dcb1d3c)
(cherry picked from commit 7d3cdf527b)
2022-03-23 08:48:17 +00:00
Deepesh Garg
c44231a432 fix: Changing item prices on converting orders/receipts to invoices
(cherry picked from commit 66ca085e39)
(cherry picked from commit a9533ef814)
2022-03-23 08:48:17 +00:00
Deepesh Garg
8fe9dbc75c Merge pull request #30368 from frappe/mergify/bp/version-13-pre-release/pr-30365
fix: Changing item prices on converting orders/receipts to invoices (backport #30365)
2022-03-23 14:17:56 +05:30
Deepesh Garg
a3f4069ad5 fix: Check if onload property exists in the form object 2022-03-23 13:58:21 +05:30
Deepesh Garg
41b2d98c3f fix: Check if onload property exists in the form object 2022-03-23 13:57:23 +05:30
Deepesh Garg
ebcd85e675 Merge pull request #30371 from frappe/mergify/bp/version-13-hotfix/pr-30361
fix: GST account not showing up in tax templates (backport #30361)
2022-03-23 13:42:44 +05:30
Deepesh Garg
0979ba0670 fix: GST account not showing up in tax templates
(cherry picked from commit 57924599da)
2022-03-23 07:47:52 +00:00
Deepesh Garg
7d3cdf527b fix: Add ignore pricelist for missing flows
(cherry picked from commit 405dcb1d3c)
2022-03-23 04:40:53 +00:00
Deepesh Garg
a9533ef814 fix: Changing item prices on converting orders/receipts to invoices
(cherry picked from commit 66ca085e39)
2022-03-23 04:40:52 +00:00
Deepesh Garg
0e3d0b0b03 fix: Add ignore pricelist for missing flows
(cherry picked from commit 405dcb1d3c)
2022-03-23 04:40:23 +00:00
Deepesh Garg
9f2868e858 fix: Changing item prices on converting orders/receipts to invoices
(cherry picked from commit 66ca085e39)
2022-03-23 04:40:22 +00:00
Ankush Menat
1b469bc341 fix: consider full integer batch nos
(cherry picked from commit 41db43cdc5)
2022-03-22 23:37:31 +05:30
Ankush Menat
4e3bb12b3a fix: ignore already fetched serial no
exclude_sr_nos is sent as JSON string of list, so load it before
operating on it.

(cherry picked from commit a18c687844)
2022-03-22 23:37:31 +05:30
Marica
315599b46f Merge pull request #30355 from frappe/mergify/bp/version-13-hotfix/pr-30336
fix: Product Filters Lookup (backport #30336)
2022-03-22 18:05:17 +05:30
mergify[bot]
7e6131280f fix: flaky salary slip email test (backport #30358) (#30360)
Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-22 17:55:26 +05:30
marination
cfce719a7d fix: Add temporary text-md variable to maintain placeholder font size in input fields
- font is unusally large for filter lookup input
2022-03-22 16:12:30 +05:30
mergify[bot]
e29757f023 test: get leave allocation records query (backport #30342) (#30353)
Co-authored-by: Ankush Menat <ankush@frappe.io>
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-03-22 15:44:45 +05:30
Marica
0f774f2f07 fix: Merge conflicts in macros
- Use `filter-lookup-wrapper` on checkbox wrapper
- Bind data value to wrapper
2022-03-22 15:39:36 +05:30
marination
37134b04d8 fix: Product Filters Lookup
- bind the right classes to the filter lookup field
- make class names more descriptive
- make filter lookup field more visible with white bg and border
- bind lookup input field js in `views.js`
- make filter lookup field functioning for atribute filters too
- added placeholder to lookup field

(cherry picked from commit f6e64c2cac)

# Conflicts:
#	erpnext/templates/includes/macros.html
2022-03-22 10:02:28 +00:00
Deepesh Garg
7cba4975ef fix: Taxes not getting fetched from item tax template 2022-03-21 21:28:41 +05:30
mergify[bot]
3ca90cc3c7 fix(ux): warning for disabled carry forwarding in Policy Assignment (backport #30331) (#30333)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-03-21 15:53:08 +05:30
mergify[bot]
34d27f1855 fix: clear "Retain Sample" and "Max Sample Quantity" in Item card if Has Batch No is uncheck (backport #30307) (#30314)
* fix: clear "Retain Sample" and "Max Sample Quantity" in Item card if Has Batch No is uncheck (#30307)

(cherry picked from commit ca8d757691)

# Conflicts:
#	erpnext/stock/doctype/item/test_item.py

* chore: conflicts

* refactor: correct usage for test decorator

Co-authored-by: HENRY Florian <florian.henry@open-concept.pro>
Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-21 15:39:04 +05:30
mergify[bot]
684e83faac fix: Future recurring period calculation for additional salary (#29581) (#30329)
* fix: Future recurring period calculation for addl salary

* fix: future recurring period calculation

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
(cherry picked from commit cedabd7242)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2022-03-21 15:00:07 +05:30
mergify[bot]
2148e7e32d fix: Payment Request Amount calculation in case of multicurrency (#30254) (#30326)
(cherry picked from commit 9abd22b408)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2022-03-21 14:57:25 +05:30
Nabin Hait
cedabd7242 fix: Future recurring period calculation for additional salary (#29581)
* fix: Future recurring period calculation for addl salary

* fix: future recurring period calculation

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-03-21 14:23:31 +05:30
Nabin Hait
c5f257da00 fix: While creating Payment Request from other forms, open a new Payment Request form without saving (#30228) 2022-03-21 14:22:10 +05:30
Nabin Hait
9abd22b408 fix: Payment Request Amount calculation in case of multicurrency (#30254) 2022-03-21 14:20:15 +05:30
mergify[bot]
71d6209a29 fix: disable deferred naming on SLE/GLE if hash method is used. (#30286) (#30315)
* fix: dont rename GLE/SLE that dont have naming series

* test: tests for deferred naming of ledgers

(cherry picked from commit c2aad115c1)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-21 12:29:21 +05:30
Deepesh Garg
621db33591 Merge pull request #30321 from frappe/mergify/bp/version-13-hotfix/pr-30317
fix: P&L account validation on cancellation (backport #30317)
2022-03-21 12:05:41 +05:30
Deepesh Garg
4337b86349 Merge pull request #30318 from frappe/mergify/bp/version-13-hotfix/pr-30304
fix: Add permission for KSA VAT documents (backport #30304)
2022-03-21 12:05:15 +05:30
Saqib Ansari
10f06d2e50 Merge pull request #30320 from frappe/mergify/bp/version-13-hotfix/pr-30287
fix: custom cash flow mapper doesn't show any data (backport #30287)
2022-03-21 11:40:06 +05:30
Deepesh Garg
1b5e936d27 fix: P&L account validation on cancellation
(cherry picked from commit 2ff6790283)
2022-03-21 05:15:39 +00:00
Saqib Ansari
8f6333cd40 refactor: convert raw sql to frappe.qb
(cherry picked from commit 00bfee97c7)
2022-03-21 05:03:19 +00:00
Saqib Ansari
4dd6a99b69 fix: custom cash flow mapper doesn't show any data
(cherry picked from commit 119273e633)
2022-03-21 05:03:19 +00:00
Ankush Menat
5ab6e3fc85 chore: bump version 2022-03-21 10:31:24 +05:30
Ankush Menat
84c5f98412 Merge branch 'version-13-pre-release' into version-13 2022-03-21 10:30:52 +05:30
Deepesh Garg
da09b64174 fix: Conflicts 2022-03-21 10:28:58 +05:30
Deepesh Garg
75458f90b5 fix: Add permission for KSA Vat documents
(cherry picked from commit 972d06555a)

# Conflicts:
#	erpnext/patches.txt
2022-03-21 04:49:05 +00:00
Syed Mujeer Hashmi
dc8674fe0d fix: Allow draft PE, PA to link to Vital Signs (#29934)
Signed-off-by: Syed Mujeer Hashmi <mujeerhashmi@4csolutions.in>

Co-authored-by: Chillar Anand <chillar@avilpage.com>
Co-authored-by: Chillar Anand <anand21nanda@gmail.com>
2022-03-21 08:42:28 +05:30
mergify[bot]
3636e596d3 refactor: remove redundant if-statement (#30311) (#30316)
(cherry picked from commit 0a015b7f70)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2022-03-20 20:24:40 +05:30
mergify[bot]
7bcad01953 fix(UX): misc serial no selector + warehouse.py refactor (backport #30309) (#30310)
* fix: set current qty as default qty for stock entry

(cherry picked from commit f4c213379e)

* fix: filter serial nos by selected batch number

(cherry picked from commit 5ec27c9055)

* fix: skip already selected serials in sr selector

(cherry picked from commit 0a533d6ccc)

* fix: sort serial nos before sending

(cherry picked from commit 4f8bb91eae)

* test: auto serial fetching

(cherry picked from commit b9eec331e3)

* refactor: batch no filter handling

(cherry picked from commit a585dff6fd)

# Conflicts:
#	erpnext/stock/doctype/serial_no/serial_no.py

* refactor: Use QB for serial fetching query

(cherry picked from commit 4b695915f4)

# Conflicts:
#	erpnext/stock/doctype/serial_no/serial_no.py

* refactor(warehouse): raw query to ORM

(cherry picked from commit 953afda01b)

* test: warehouse conversion and treeview test

(cherry picked from commit 684d9d66d1)

* perf: Single query to delete bins instead of `N`

(cherry picked from commit 4859574233)

* chore: resolve conflicts

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-20 19:25:10 +05:30
Ankush Menat
230d171c58 chore: add change log for v13.23.0 2022-03-20 15:18:33 +05:30
Devarsh Bhatt
cca9668309 fix: Allow on Submit for Material Request Item Required Date (#30174)
* fix: Allow on Submit for Material Request Item Required Date

* chore: whitespace

Co-authored-by: Ankush Menat <ankushmenat@gmail.com>
2022-03-19 13:02:21 +05:30
Deepesh Garg
1c36b1a923 Merge pull request #30293 from frappe/mergify/bp/version-13-pre-release/pr-30284
fix: Cleanup and fixes in Dimension-wise Accounts Balance Report (backport #30284)
2022-03-18 17:37:12 +05:30
mergify[bot]
ec79e13b61 fix: respect db multi_tenancy while fetching precision (#30301) (#30303)
[skip ci]

(cherry picked from commit 5a9bf9ffd6)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-18 17:32:09 +05:30
mergify[bot]
3d35c69a2d fix: respect db multi_tenancy while fetching precision (#30301) (#30302)
[skip ci]

(cherry picked from commit 5a9bf9ffd6)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-18 17:32:01 +05:30
Deepesh Garg
88b2447297 Merge pull request #30296 from frappe/mergify/bp/version-13-hotfix/pr-30266
fix: Validate income/expense account in sales and purchase invoice (backport #30266)
2022-03-17 18:55:21 +05:30
Deepesh Garg
6c23b306d9 Merge pull request #30297 from frappe/mergify/bp/version-13-pre-release/pr-30266
fix: Validate income/expense account in sales and purchase invoice (backport #30266)
2022-03-17 18:54:50 +05:30
Deepesh Garg
0269bc9191 fix: Test case
(cherry picked from commit 4237e5d928)
2022-03-17 13:03:41 +00:00
Deepesh Garg
c0183289f5 fix: Validate income/expense account in sales and purchase invoice
(cherry picked from commit 06936cf1c0)
2022-03-17 13:03:41 +00:00
Deepesh Garg
2cf19aa19d fix: Test case
(cherry picked from commit 4237e5d928)
2022-03-17 13:03:20 +00:00
Deepesh Garg
935d2e3d64 fix: Validate income/expense account in sales and purchase invoice
(cherry picked from commit 06936cf1c0)
2022-03-17 13:03:20 +00:00
mergify[bot]
face53ae8e feat: Create single PL/DN from several SO. (backport #30238) (#30289)
* feat: Create single PL/DN from several SO. New PR from latest develop to avoid rebase

(cherry picked from commit 61eb9b6c68)

* to enable selection of SO irrespective of customer,removed validation for customer

(cherry picked from commit 466df6bdb7)

* fixed spacings

(cherry picked from commit f33a725a9e)

* added new field - Picked Qty, in Sales Order Item

(cherry picked from commit a68213d82e)

* Added new field in SO - % Picked

(cherry picked from commit 9f7fee7a4f)

* removed semicolon for break statement

(cherry picked from commit e970616b51)

* as per review comments - changed for loop

(cherry picked from commit 0211f27e83)

* corrected spacing

(cherry picked from commit a12895ec03)

Co-authored-by: Krithi Ramani <krithi.ramani@gmail.com>
2022-03-17 18:17:16 +05:30
Deepesh Garg
c20fc51921 Merge pull request #30294 from frappe/mergify/bp/version-13-pre-release/pr-30261
fix: Error in bank reconciliation statement (backport #30261)
2022-03-17 18:11:14 +05:30
Deepesh Garg
e60145e10a Merge pull request #30295 from frappe/mergify/bp/version-13-hotfix/pr-30261
fix: Error in bank reconciliation statement (backport #30261)
2022-03-17 18:11:04 +05:30
Deepesh Garg
aa1732a212 fix: Error in bank reco statement
(cherry picked from commit fbcb413d96)
2022-03-17 12:21:03 +00:00
Deepesh Garg
9e8b8305e9 fix: Error in bank reco statement
(cherry picked from commit fbcb413d96)
2022-03-17 12:00:05 +00:00
Deepesh Garg
fe88cfb80d fix: Remove comments
(cherry picked from commit cab69fe1f2)
2022-03-17 11:58:11 +00:00
Deepesh Garg
eac07238d9 fix: Clean and fixes in Dimension-wise Accounts Balance Report
(cherry picked from commit 08a06ce5c6)
2022-03-17 11:58:10 +00:00
Deepesh Garg
65d0189f71 Merge pull request #30284 from deepeshgarg007/dimension_wise_balance_report
fix: Clean and fixes in Dimension-wise Accounts Balance Report
2022-03-17 17:27:01 +05:30
Ankush Menat
d8b7693ea9 Merge pull request #30291 from ankush/13_23_pre_release
chore: merge version-13-hotfix to version-13-pre-release
2022-03-17 16:31:00 +05:30
mergify[bot]
3b1033c315 test: fix holiday list creation causing flaky tests (backport #30260) (#30288)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-03-17 15:39:02 +05:30
Deepesh Garg
cab69fe1f2 fix: Remove comments 2022-03-17 12:57:12 +05:30
Deepesh Garg
08a06ce5c6 fix: Clean and fixes in Dimension-wise Accounts Balance Report 2022-03-17 12:56:43 +05:30
Rucha Mahabal
15413cf835 fix: Non Profit fixes (#30280) 2022-03-17 12:52:02 +05:30
mergify[bot]
0becc1e231 refactor: call repost directly during tests (#30277) (#30279)
enqueue(now=frappe.flags.in_test) is always true in test, this change avoids
confusion.

(cherry picked from commit 76187d175f)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-16 23:01:38 +05:30
mergify[bot]
64b646050e refactor: removed unrequired code and test for standalone delivery note serial return (#30276) (#30278)
(cherry picked from commit 2a0ca7c91a)

Co-authored-by: Noah Jacob <noahjacobkurian@gmail.com>
2022-03-16 19:51:22 +05:30
Ankush Menat
f3af5dd384 test: packed item return scenarios
(cherry picked from commit b46d6e3c05)
2022-03-16 14:02:17 +05:30
Ankush Menat
cde3c685ee revert: "fix: updated packed_items getting fetched on Sales Return / Credit Note (#28607)"
This reverts commit 20216fa9f1.

(cherry picked from commit b781e8b7d1)
2022-03-16 14:02:17 +05:30
Saqib Ansari
b8fc06f633 Merge pull request #30270 from frappe/mergify/bp/version-13-hotfix/pr-30269
fix: Do not update ignore pricing rule check implicitly (backport #30269)
2022-03-16 13:01:54 +05:30
Saqib Ansari
461506a7ff Merge pull request #30267 from frappe/mergify/bp/version-13-hotfix/pr-30265
feat: add German translations (backport #30265)
2022-03-16 13:01:32 +05:30
Deepesh Garg
f27fd605f5 fix: Do not update ignore prcing rule check implicitly
(cherry picked from commit 1f79b47a17)
2022-03-16 07:03:16 +00:00
Saqib Ansari
3bcef50b15 Merge pull request #30262 from frappe/mergify/bp/version-13-hotfix/pr-30244
fix: incorrect debit credit amount in presentation currency (backport #30244)
2022-03-16 10:55:04 +05:30
Samuel Danieli
29fa902b33 feat: add German translations
(cherry picked from commit bbe5739547)
2022-03-16 05:24:45 +00:00
Saqib Ansari
510668f78a test: foreign_account_balance_after_exchange_rate_revaluation
(cherry picked from commit 12d99ed69a)
2022-03-15 13:10:27 +00:00
Saqib Ansari
8663a776dd fix: incorrect debit credit amount in presentation currency
(cherry picked from commit 83a5fae591)
2022-03-15 13:10:26 +00:00
Marica
69bf64e238 Merge pull request #30259 from frappe/mergify/bp/version-13-hotfix/pr-30258
fix: Sub-Category Routing in Item Group Page Listing pills (backport #30258)
2022-03-15 17:47:40 +05:30
marination
a56fd20ab4 fix: Sub-Categpry Routing in Item Group Page Listing pills
- Use absolute route even 3-4 sub-category levels down
- Remove scroll from category pills due to accessibility issues
- Arrange sub-category pills alphabetically

(cherry picked from commit 8264d6b0bc)
2022-03-15 12:00:33 +00:00
mergify[bot]
2c5e76d542 fix(pos): loyalty points in case of returned pos invoice (#30257) 2022-03-15 17:28:21 +05:30
rohitwaghchaure
5d458d6eb7 Merge pull request #30243 from akurungadam/jc-fix
fix: Job Card sub operations status and list view
2022-03-15 15:20:56 +05:30
mergify[bot]
6ef08545b6 fix: Leave Policy Assignment creation patch (backport #30215) (#30252)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-03-15 12:24:08 +05:30
mergify[bot]
0133b2a145 fix: salary slip amount rounding errors (backport #30248) (#30251)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-03-15 11:41:38 +05:30
Deepesh Garg
f48caf4944 Merge pull request #30246 from frappe/mergify/bp/version-13-hotfix/pr-30230
fix: KSA E-Invoice QR Code showing wrong VAT amount (backport #30230)
2022-03-15 09:49:55 +05:30
Deepesh Garg
5e4ab346c7 Merge pull request #30245 from frappe/mergify/bp/version-13-hotfix/pr-30206
fix: Do not update ignore prcing rule check implicitly (backport #30206)
2022-03-15 09:49:35 +05:30
Deepesh Garg
ec92551f7c Update erpnext/regional/saudi_arabia/utils.py
Co-authored-by: Saqib Ansari <nextchamp.saqib@gmail.com>
(cherry picked from commit e33d4713cd)
2022-03-14 16:17:46 +00:00
Deepesh Garg
c5de90320f fix: Itemised tax rate updation
(cherry picked from commit be56efad26)
2022-03-14 16:17:46 +00:00
Deepesh Garg
fc298770e9 fix: Linting Issue
(cherry picked from commit 3cc2e53b08)
2022-03-14 16:17:46 +00:00
Deepesh Garg
8b077106f9 fix: KSA E-Invoice QR Code showing wrong VAT amount
(cherry picked from commit b37559c535)
2022-03-14 16:17:45 +00:00
Deepesh Garg
b0e178a29d fix: Do not update ignore prcing rule check implicitly
(cherry picked from commit 96b5cedcf8)
2022-03-14 16:16:50 +00:00
Anoop Kurungadam
7b8723445e fix: job card - sub operations table status misbehaviour on pause / resume Job Card 2022-03-14 19:34:18 +05:30
Anoop Kurungadam
fa32fc3c83 fix: show status in job card list view in Draft mode 2022-03-14 19:33:58 +05:30
Ankush Menat
1dbb654500 test: refactor BOM quality template test 2022-03-14 18:56:33 +05:30
Florian HENRY
d8de8caa1e fix: BOM - clear Quality Inspection Template according to Inspection Quality Required
Squashed commit of the following:

commit b73fa210b234d2c8067db2c32f94f362b89afe5a
Author: Florian HENRY <florian.henry@open-concept.pro>
Date:   Mon Mar 14 08:30:24 2022 +0100

    add json tes

commit 984d015a7d9aceb6ea64be7ed9f1cc0caa356714
Author: Florian HENRY <florian.henry@open-concept.pro>
Date:   Mon Mar 14 08:30:06 2022 +0100

    better test

commit 42061146658598da02eda7cb781bd2cf44c8ca34
Author: Florian HENRY <florian.henry@open-concept.pro>
Date:   Fri Mar 11 16:12:57 2022 +0100

    update test

commit 6259c0957566600b044fcd4a5e14e69cdff71020
Author: Florian HENRY <florian.henry@open-concept.pro>
Date:   Fri Mar 11 13:59:13 2022 +0100

    update test

commit cbc4ad9566f6f7f01dd622651458fea309d80954
Merge: faa44b3fdd 94d0f8d4e7
Author: Florian HENRY <florian.henry@open-concept.pro>
Date:   Fri Mar 11 12:46:48 2022 +0100

    Merge branch 'develop' of https://github.com/frappe/erpnext into dev_fix_30190

commit faa44b3fdd983adfc04bbf04a7ebfef114501bb3
Author: Florian HENRY <florian.henry@open-concept.pro>
Date:   Fri Mar 11 12:03:35 2022 +0100

    fix: clear Quality Inspection Template according to Inspection Quality Required
2022-03-14 18:56:33 +05:30
mergify[bot]
767a02cd15 fix: cannot create multicurrency sales order with product bundles (#30222) 2022-03-14 18:33:42 +05:30
mergify[bot]
34d6031601 fix(pos): do not reset mode of payments in case of consolidation (backport #30198) (#30218) 2022-03-14 18:30:41 +05:30
Marica
9b2cb23b22 Merge pull request #30232 from frappe/mergify/bp/version-13-hotfix/pr-30229
fix: Add missing currency option in Supplier Quotation's `rounded_total` field (backport #30229)
2022-03-14 17:22:06 +05:30
Ankush Menat
2d8bbf1112 Merge pull request #30237 from frappe/mergify/bp/version-13-hotfix/pr-30207
fix: Do not consider cancelled entries (backport #30207)
2022-03-14 17:20:46 +05:30
Deepesh Garg
f23810749d fix: Do not consider cancelled entries
(cherry picked from commit 6308e1be91)
2022-03-14 11:49:53 +00:00
Ankush Menat
4768f4f278 fix(ux): negative stock warning
(cherry picked from commit 02d64a32c2)
2022-03-14 17:18:06 +05:30
Ankush Menat
fab8e16537 Merge pull request #30235 from frappe/mergify/bp/version-13-pre-release/pr-30216
fix: max_qty validation condition in WO (backport #30216)
2022-03-14 17:09:24 +05:30
Sagar Sharma
46f9503a5c test: add test for planned_qty
(cherry picked from commit b22bdc5ff7)
2022-03-14 11:38:43 +00:00
Sagar Sharma
18d7c77bad fix: max_qty validation condition
(cherry picked from commit d198c488a4)
2022-03-14 11:38:43 +00:00
Ankush Menat
aab677c26f Merge pull request #30216 from s-aga-r/fix/production-plan/validation/max_qty
fix: max_qty validation condition in WO
2022-03-14 17:08:21 +05:30
marination
d5b142e5ca chore: Re-arrange fields for consistency with base currency LHS
(cherry picked from commit a579a211fd)
2022-03-14 11:31:19 +00:00
marination
017624c270 fix: Add missing currency option in Supplier Quotation's rounded_total field
(cherry picked from commit dcd88ddc87)
2022-03-14 11:31:19 +00:00
Sagar Sharma
b22bdc5ff7 test: add test for planned_qty 2022-03-14 16:28:23 +05:30
Ankush Menat
1fd96f379f test(refactor): use FrappeTestCase
(cherry picked from commit b0d1e6db54)
2022-03-14 16:20:04 +05:30
mergify[bot]
2e79c4fd59 fix(ux): remove get item buttons from submitted production plan (#30224)
(cherry picked from commit 1af13ca4bf)

fix(patch): remove dead links to ProdPlan Item

(cherry picked from commit d3e90ed8c8)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-14 15:17:46 +05:30
Ankush Menat
468964a3f7 ci: Revert fail on merge conflict label
This reverts commit d32f229483.
2022-03-14 14:43:15 +05:30
Nabin Hait
98a67967a3 fix: Search query of payroll entry reference in Journal Entry (#30225) 2022-03-14 14:40:00 +05:30
Saqib Ansari
75061cae66 Merge pull request #30220 from nextchamp-saqib/backport-#26872
fix: pos return payment mode issue (#26872)
2022-03-14 13:41:41 +05:30
Subin Tom
a1c9d1c67f fix: pos return payment mode issue (#26872) 2022-03-14 12:53:26 +05:30
Deepesh Garg
2e10e9ec33 Merge pull request #30210 from frappe/mergify/bp/version-13-hotfix/pr-30039
fix: Nil and Exempted values in GSTR-3B Report (backport #30039)
2022-03-14 12:51:31 +05:30
Deepesh Garg
5c285bc019 Merge pull request #30211 from frappe/mergify/bp/version-13-hotfix/pr-29979
fix: Multi-currency bank reconciliation fixes (backport #29979)
2022-03-14 12:51:14 +05:30
Ankush Menat
7aca949a33 fix(ux): skip items without batch series
(cherry picked from commit 941ea1ec74)
2022-03-14 12:50:59 +05:30
Ankush Menat
aadc2a8849 test: negative fifo test
(cherry picked from commit 91fd9d917a)
2022-03-14 12:50:59 +05:30
Saqib Ansari
1120a0f72f Merge pull request #30219 from frappe/mergify/bp/version-13-hotfix/pr-30217
fix: cannot create purchase order from sales order (backport #30217)
2022-03-14 12:45:51 +05:30
Saqib Ansari
1cee08f3e9 fix: cannot create purchase order from sales order
(cherry picked from commit 58804b8436)
2022-03-14 07:05:59 +00:00
Sagar Sharma
d198c488a4 fix: max_qty validation condition 2022-03-14 12:05:34 +05:30
Ankush Menat
fc257fa58d test: actual bom naming test
(cherry picked from commit 94d0f8d4e7)
2022-03-14 09:58:14 +05:30
Rohan Bansal
c8ca92e406 fix: cancelled document check
(cherry picked from commit 67d8a7ba86)
2022-03-14 09:58:14 +05:30
Ankush Menat
1a2a03ed98 refactor: split versioning code for testability
(cherry picked from commit 6b58d53403)
2022-03-14 09:58:14 +05:30
Rohan Bansal
6a031a87da fix: improve bom autoname logic
(cherry picked from commit 7f2670941c)
2022-03-14 09:58:14 +05:30
Ankush Menat
25df914bc6 test: bom for item_code that is >VARCHAR_LEN
(cherry picked from commit e2c99e02a9)
2022-03-14 09:58:14 +05:30
Rohan Bansal
404a82121d fix: if an item code is too long, truncate before setting BOM name
(cherry picked from commit d9c91748f4)
2022-03-14 09:58:14 +05:30
Rucha Mahabal
164d28a87a Merge pull request #30212 from frappe/mergify/bp/version-13-hotfix/pr-29439 2022-03-13 21:46:50 +05:30
Rucha Mahabal
465c117708 fix: conflicts 2022-03-13 21:05:06 +05:30
Rucha Mahabal
d50dbca844 fix: flaky tests
(cherry picked from commit d61c437588)
2022-03-13 15:26:33 +00:00
Rucha Mahabal
70febe468b fix: add more type hints
(cherry picked from commit 558650bc3a)
2022-03-13 15:26:33 +00:00
Rucha Mahabal
9449d11a4c fix: simplify insufficient leave balance message
(cherry picked from commit 8173e6a8ea)
2022-03-13 15:26:32 +00:00
Rucha Mahabal
c66a66e090 fix: conflicts
(cherry picked from commit b2c549a464)
2022-03-13 15:26:32 +00:00
Rucha Mahabal
d4301675d1 chore: linter issue
(cherry picked from commit 921d6b25d7)
2022-03-13 15:26:32 +00:00
Rucha Mahabal
fab0153e34 test: separate leave ledger entries for leave applications across allocations
(cherry picked from commit 97b7b5012e)
2022-03-13 15:26:31 +00:00
Rucha Mahabal
75833243ae fix: boundary determination for separate ledger entries
(cherry picked from commit 70239158b9)
2022-03-13 15:26:31 +00:00
Rucha Mahabal
b63cedc7d7 test: leave application validations
(cherry picked from commit 6755d6e6f5)
2022-03-13 15:26:31 +00:00
Rucha Mahabal
bfedac47e6 fix: clearer validation/warning messages for insufficient balance in leave application
(cherry picked from commit a504ffcc4c)
2022-03-13 15:26:31 +00:00
Rucha Mahabal
a30ec8bef6 feat: split ledger entries for applications created across allocations
- fix: ledger entry for expiring cf leaves not considering holidays

(cherry picked from commit c0f1e269e4)
2022-03-13 15:26:31 +00:00
Rucha Mahabal
51b17cfbc0 fix: add type hints for employee leave balance report
(cherry picked from commit 430bf00458)
2022-03-13 15:26:30 +00:00
Rucha Mahabal
4cd89c3dac test: get leave details for leave application dashboard
(cherry picked from commit 3f3b1766c2)

# Conflicts:
#	erpnext/hr/doctype/leave_application/test_leave_application.py
2022-03-13 15:26:30 +00:00
Rucha Mahabal
758e095ffd test: fix test test_leave_balance_near_allocaton_expiry
(cherry picked from commit a58dfecb23)
2022-03-13 15:26:30 +00:00
Rucha Mahabal
d9ec973096 fix: earned leave policy assignment test
(cherry picked from commit aaa1ae94f2)
2022-03-13 15:26:30 +00:00
Rucha Mahabal
b1d3d8226c fix: leave application dashboard
- total leaves allocated considering cancelled leaves

- optional plural for leave category labels

- show dashboard only once from date is set, else it fetches all allocations till date and generates incorrect balance

- change pending leaves to 'Leaves Pending Approval' for better context

- update labels in Salary Slip Leave Details table

(cherry picked from commit 942511cfff)
2022-03-13 15:26:29 +00:00
Rucha Mahabal
73b104aa45 test: Employee Leave Balance Summary
(cherry picked from commit 88141d6116)
2022-03-13 15:26:29 +00:00
Rucha Mahabal
49434e443c chore: remove unused imports, sort imports, fix sider
(cherry picked from commit c7d594984a)
2022-03-13 15:26:28 +00:00
Rucha Mahabal
6af8e9790b test: employee leave balance report
- fix expired leaves calculation when filters span across 2 different allocation periods

(cherry picked from commit c050ce49c2)

# Conflicts:
#	erpnext/payroll/doctype/salary_slip/test_salary_slip.py
2022-03-13 15:26:28 +00:00
Rucha Mahabal
e96b661ca1 fix: show actual balance instead of consumption balance in opening balance
- not changing opening balance based on remaining days

(cherry picked from commit dbfa463738)
2022-03-13 15:26:28 +00:00
Rucha Mahabal
420cb8269c fix: sort imports, sider issues
(cherry picked from commit b5c686ac40)
2022-03-13 15:26:28 +00:00
Rucha Mahabal
4632d13dca refactor: balance in Balance Summary report near allocation expiry date
- Leave Balance shows minimum leaves remaining after comparing with remaining days for allocation expiry causing ambiguity

- refactor remaining leaves calculation to return both, actual leave balance and balance for consumption

- show actual balance in leave application, use balance for consumption only in validations

(cherry picked from commit 55ac8519bf)
2022-03-13 15:26:27 +00:00
Rucha Mahabal
60eb38c911 refactor: Leaves Taken calculation
- fix issue where Leaves Taken also adds leaves expiring on boundary date as leaves taken, causing ambiguity

- remove unnecessary `skip_expiry_leaves` function

- `get_allocation_expiry` considering cancelled entries too

(cherry picked from commit 26b40e7cfd)
2022-03-13 15:26:27 +00:00
Rucha Mahabal
d7a75a695d fix: expired leaves not calculated properly because of newly created expiry ledger entries
(cherry picked from commit 1ea749cf1a)
2022-03-13 15:26:27 +00:00
Rucha Mahabal
f7b06a622f refactor: Employee Leave Balance Report
- incorrect opening balance on boundary allocation dates

- carry forwarded leaves accounted in leaves allocated column, should be part of opening balance

- leaves allocated column also adds expired leave allocations causing negative balances, should only consider non-expiry records

(cherry picked from commit 538b64b1fa)
2022-03-13 15:26:27 +00:00
Deepesh Garg
8a567b8d8c fix: Multi-currency bank reconciliation fixes
(cherry picked from commit cbb5ffb6fe)
2022-03-13 14:57:26 +00:00
Deepesh Garg
39d6cebf1a fix: Nil and Exempted values in GSTR-3B Report
(cherry picked from commit abe580e8b0)
2022-03-13 14:31:37 +00:00
Ankush Menat
2a00380e5b fix: attach sr no si standalone credit note
(cherry picked from commit 1a256c62c4)
2022-03-13 19:24:41 +05:30
Ankush Menat
625034a016 test: standalone SI creates and attaches serial nos
(cherry picked from commit 1c37d2711a)
2022-03-13 19:24:41 +05:30
Deepesh Garg
d71a8886b4 Merge pull request #30205 from frappe/mergify/bp/version-13-hotfix/pr-30152
fix: Sales and Purchase return optimization (backport #30152)
2022-03-13 17:49:19 +05:30
Deepesh Garg
abc2dd9e3e fix: Update modified timestamp
(cherry picked from commit a5befb6bf8)
2022-03-13 11:44:13 +00:00
Deepesh Garg
f31a7bde82 fix: Update party type
(cherry picked from commit e9d458b822)
2022-03-13 11:44:13 +00:00
Deepesh Garg
5eeb09c84e fix: Sales and Purchase retrun optimization
(cherry picked from commit 395b15058c)
2022-03-13 11:44:13 +00:00
Deepesh Garg
d47b3517f3 Merge pull request #30188 from frappe/mergify/bp/version-13-hotfix/pr-30181
fix: Shipping rule application fixes (backport #30181)
2022-03-13 17:14:07 +05:30
Deepesh Garg
44fd94f6b2 fix: Shipping rule application fixes
(cherry picked from commit d596e0e4df)
2022-03-11 06:35:03 +00:00
Mostafa Fekry
3a5f5d5cd0 fix: e_commerce_settings.js
Update get meta from Item to Website Item
To allow Website Item Fields to display meta fields
2022-03-10 16:05:08 +02:00
Ankush Menat
a04c7fc27d test: submit PR directly
(cherry picked from commit 472fe8e319)
2022-03-10 18:27:57 +05:30
Ankush Menat
b13389b99a test: flaky MR report test
all test records are on same day so sorting was random in report rows.

(cherry picked from commit 6c54be0dcd)
2022-03-10 18:27:57 +05:30
Ankush Menat
e428c36053 fix: only update valuation rate if not None
(cherry picked from commit 7dd10367f4)
2022-03-10 18:27:57 +05:30
mergify[bot]
f06554236f fix: fetch new fields from routing to bom (backport #30169) (#30171)
* fix: fetch new fields in bom from routing

(cherry picked from commit 18e2a33a9b)

* fix: dont hardcode hour rate precision

(cherry picked from commit 362102e802)

# Conflicts:
#	erpnext/manufacturing/doctype/bom_operation/bom_operation.json

* fix: resolve conflict

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-10 18:09:09 +05:30
Ankush Menat
33ddbb4f3c fix: handle duplicate bins during item merge renames
(cherry picked from commit 73901aad6f)
2022-03-10 17:23:23 +05:30
Ankush Menat
de5500817f test: refactor item merge test and disable commits
(cherry picked from commit b4d4ae6aa3)
2022-03-10 17:23:23 +05:30
mergify[bot]
a5b25db880 fix: dont reset UOM in MR on every get_item_detail call (#30164) (#30165)
(cherry picked from commit 4126455975)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-10 15:44:25 +05:30
Saqib Ansari
e89c181179 Merge pull request #30162 from frappe/mergify/bp/version-13-hotfix/pr-30103
fix: Ignore missing customer group while fetching price list (backport #30103)
2022-03-10 15:29:34 +05:30
Marica
584f0698ff Merge pull request #30163 from frappe/mergify/bp/version-13-hotfix/pr-30160
fix: 'save_quotations_as_draft' checkbox not honoured (backport #30160)
2022-03-10 15:12:02 +05:30
Marica
b529b5c0cb fix: Remove extra empty line after frappe imports 2022-03-10 14:53:48 +05:30
Marica
aec1916a0f fix: Merge Conflicts in test_shopping_cart imports 2022-03-10 14:49:05 +05:30
marination
62b50b6b63 fix: 'save_quotations_as_draft' checkbox not honoured
- Make sure `request_for_quotation` considers `save_quotations_as_draft`
- Added test for checkout disabled quote

(cherry picked from commit a13e06156b)

# Conflicts:
#	erpnext/e_commerce/shopping_cart/test_shopping_cart.py
2022-03-10 09:09:00 +00:00
mergify[bot]
4febcd86f8 fix: flaky tests (backport #30154) (#30161)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-03-10 14:38:08 +05:30
Saqib Ansari
3f0a4d9921 fix: Ignore missing customer group while fetching price list
(cherry picked from commit e5fb871ef4)
2022-03-10 09:06:42 +00:00
ChillarAnand
bb0be1699b fix: Ignore missing customer group while fetching price list
(cherry picked from commit 9ace7d606c)
2022-03-10 09:06:42 +00:00
Deepesh Garg
5e6879a7f7 Merge pull request #30159 from frappe/mergify/bp/version-13-hotfix/pr-29696
fix: do not reset asset_category (backport #29696)
2022-03-10 14:19:55 +05:30
Himanshu
5081438748 fix: do not reset asset_category (#29696)
(cherry picked from commit 5193a63781)
2022-03-10 08:15:21 +00:00
mergify[bot]
6c21642f32 fix(psoa): no such element: dict object['account'] (#30156) 2022-03-10 13:36:52 +05:30
Saqib Ansari
92a59fabe5 chore: imports (#30157)
Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-10 13:26:11 +05:30
Saqib Ansari
903e5d03a7 Merge pull request #30151 from frappe/mergify/bp/version-13-hotfix/pr-30145
fix(psoa): add company filter to account (backport #30145)
2022-03-10 13:16:44 +05:30
Saqib Ansari
cef8820369 fix(psoa): add company filter to account
(cherry picked from commit fc42041f8f)
2022-03-10 05:25:24 +00:00
mergify[bot]
31ba09b3d0 fix: program enrollment button labels (backport #30148) (#30149)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-03-10 10:07:47 +05:30
Marica
c9d0692fb4 Merge pull request #30141 from frappe/mergify/bp/version-13-hotfix/pr-30091
feat: Include child item group products in Item Group Page & cleanup (backport #30091)
2022-03-09 18:11:35 +05:30
mergify[bot]
e3387a06b6 fix: customer credit limit validation on update (#30126)
(cherry picked from commit a0dc79332d)

Co-authored-by: Saqib Ansari <nextchamp.saqib@gmail.com>
2022-03-09 17:49:34 +05:30
mergify[bot]
eb90d50946 fix: ignore non-unique swift numbers while migrating (#30139)
(cherry picked from commit 4d8798b0ea)

Co-authored-by: Saqib Ansari <nextchamp.saqib@gmail.com>
2022-03-09 17:49:11 +05:30
mergify[bot]
63e7d8c651 perf(asset): fetch only distinct depreciable assets (#30138)
(cherry picked from commit 66f20209f6)

Co-authored-by: Saqib Ansari <nextchamp.saqib@gmail.com>
2022-03-09 17:48:49 +05:30
Marica
458f264575 fix: Remove extra line 2022-03-09 17:35:01 +05:30
Marica
6241f9b9aa Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-30091 2022-03-09 17:33:37 +05:30
Marica
db38c36bba Merge pull request #30144 from frappe/mergify/bp/version-13-hotfix/pr-30000
fix(translation) - correction for assets translation (backport #30000)
2022-03-09 17:33:13 +05:30
mergify[bot]
d68cf1885f test: Added test for monthly attendance report (backport #29989) (#30143)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
Co-authored-by: Chillar Anand <anand21nanda@gmail.com>
2022-03-09 17:32:29 +05:30
Marica
4b4b104843 fix: Merge conflicts 2022-03-09 17:29:38 +05:30
hrzzz
f721a2929f fix(translation) - correction for translation
(cherry picked from commit 16de29a3cb)

# Conflicts:
#	erpnext/assets/doctype/asset/asset_dashboard.py
2022-03-09 11:53:10 +00:00
mergify[bot]
51f7ca856b revert: BU Schlüssel (a21f76f) (#30142)
(cherry picked from commit 363ed9ccba)

Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com>
2022-03-09 16:51:58 +05:30
marination
ca41263736 fix: Linter
(cherry picked from commit 7b37a74023)
2022-03-09 11:16:56 +00:00
marination
c8026eb9df test: Test include_descendants in Item Group Product Listing
- Also made include_descendants field's visibility dependant on show_in_website

(cherry picked from commit 3507cf5985)
2022-03-09 11:16:55 +00:00
marination
7f234c7e34 feat: Include child item group products in Item Group Page & cleanup
- Added 'Include descendants' checkbox, which will pull child item group products too
- Build item group filters in query engine file
- Include logic in filter engine
- Clean up Website section of Item Group page (UX)
- Add util to fetch child item groups including self

(cherry picked from commit b2755f6fdd)
2022-03-09 11:16:55 +00:00
Deepesh Garg
b503b83960 Merge pull request #30137 from frappe/mergify/bp/version-13-hotfix/pr-30136
fix: Remove tax invoice no field (backport #30136)
2022-03-09 16:42:01 +05:30
Deepesh Garg
8d7418171b fix: Update timestamp
(cherry picked from commit 9b8258479c)
2022-03-09 10:51:05 +00:00
Deepesh Garg
856534c8a0 fix: Remove tax invoice no field
(cherry picked from commit 8a2fe7a2e3)
2022-03-09 10:51:05 +00:00
Ankush Menat
76376d7ad7 chore: bump version 2022-03-09 15:41:58 +05:30
Deepesh Garg
5c7b2e2616 Merge pull request #30133 from frappe/mergify/bp/version-13-hotfix/pr-30096
fix(ux): Improve label for better understanding (backport #30096)
2022-03-09 15:40:26 +05:30
mergify[bot]
a12a3bd441 fix: dont fetch entire barcode table in get_item_details (#30131) (#30135)
(cherry picked from commit 64905188c4)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-09 15:38:38 +05:30
mergify[bot]
58f41722b6 fix: dont fetch entire barcode table in get_item_details (#30131) (#30134)
(cherry picked from commit 64905188c4)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-09 15:38:26 +05:30
Deepesh Garg
4a5ea55965 fix(ux): Improve label for better understanding
(cherry picked from commit f3a95d3c27)
2022-03-09 10:05:11 +00:00
Deepesh Garg
ab6cdbde14 Merge pull request #29949 from frappe/mergify/bp/version-13-hotfix/pr-29865
feat: Bank Reconciliation for loan documents (backport #29865)
2022-03-09 15:23:38 +05:30
Mohamed-D-Ouf
3d8eaa5392 fix: add filters in default_discount_account (#30095) 2022-03-09 11:25:36 +05:30
Ankush Menat
ba21e2e08d Merge pull request #30123 from frappe/mergify/bp/version-13-hotfix/pr-30080
fix: Item-wise sales history report (backport #30080)
2022-03-09 11:16:34 +05:30
Ankush Menat
b3fd6652c0 Merge pull request #30122 from frappe/mergify/bp/version-13-hotfix/pr-30102
fix: Ambigous column in picklist query (backport #30102)
2022-03-09 11:15:59 +05:30
Deepesh Garg
8fc105a186 fix: Item-wise sales history report
(cherry picked from commit e2c144e9e3)
2022-03-09 04:48:32 +00:00
Deepesh Garg
95792d8f32 fix: Remove unintentional changes
(cherry picked from commit d9d4c2ce79)
2022-03-09 04:48:07 +00:00
Deepesh Garg
b9ccbd50b4 fix: Ambigous column in picklist query
(cherry picked from commit 517fbf1d1f)
2022-03-09 04:48:06 +00:00
Deepesh Garg
3aace2f420 fix: Reload doctypes 2022-03-09 10:11:01 +05:30
mergify[bot]
07b8dd0300 fix: leave allocation records query (backport #30118) (#30120)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-03-08 23:48:44 +05:30
mergify[bot]
e8f17e582b test: Fix flaky tests (backport #30107) (#30119)
Co-authored-by: Chillar Anand <anand21nanda@gmail.com>
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-03-08 23:25:59 +05:30
Deepesh Garg
6a4c0234f5 Merge pull request #29918 from frappe/mergify/bp/version-13-hotfix/pr-29740
fix: Multiple fixes in Gross Profit report (backport #29740)
2022-03-08 23:01:40 +05:30
Deepesh Garg
ac7f1820c5 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-29740 2022-03-08 21:41:53 +05:30
mergify[bot]
e5c82d018f fix: Get MRs that are yet to be received but fully ordered in Report Requested Items to Order and Receive (backport #29987) (#30083)
* fix: Get MRs that are yet to be received but fully ordered in Report

- Remove incorrect query clause that only check if ordered qty < 100
- MR should be visible in report until fully received (cycle complete)

(cherry picked from commit d3b0ca30c6)

* refactor: Convert to QB, added test file, removed white space

- Converted mysql raw query to qb
- Test file for Report Requested Items to Order and Receive
- Removed white space and edited copyright year

(cherry picked from commit e6952cb7f9)

* fix: Sider and Linter

(cherry picked from commit ac425722e2)

* fix: linter (imports alphabetical)

(cherry picked from commit 54b3676f35)

Co-authored-by: marination <maricadsouza221197@gmail.com>
2022-03-08 18:00:19 +05:30
Deepesh Garg
dc8fd6d0ff Merge pull request #30104 from frappe/mergify/bp/version-13-hotfix/pr-29940
fix: Item discounts for quotation and other docs (backport #29940)
2022-03-08 14:58:43 +05:30
Rohit Waghchaure
ab0dcd7146 Merge branch 'version-13-pre-release' into version-13 2022-03-08 14:57:18 +05:30
Rohit Waghchaure
a7776f85ef bumped to version 13.22.0 2022-03-08 15:17:18 +05:50
Deepesh Garg
7de6f0a9c3 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-29865 2022-03-08 14:02:26 +05:30
Deepesh Garg
07db36b23d Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-29940 2022-03-08 14:02:00 +05:30
Deepesh Garg
c46e76c543 Merge pull request #30109 from deepeshgarg007/change_log_v13_22
chore: change log for v13.22.0
2022-03-08 13:06:29 +05:30
Deepesh Garg
9538c94b69 chore: change log for v13.22.0 2022-03-08 12:48:27 +05:30
Saqib Ansari
86afbf98a3 Merge pull request #30108 from frappe/mergify/bp/version-13-pre-release/pr-30097
fix(pos): multiple pos round off cases (backport #30097)
2022-03-08 12:40:16 +05:30
Saqib Ansari
150d1e6e60 fix(pos): multiple pos round off cases
(cherry picked from commit 17445c7e04)
2022-03-08 06:29:56 +00:00
Saqib Ansari
f7f114789d Merge pull request #30106 from frappe/mergify/bp/version-13-hotfix/pr-30097
fix(pos): multiple pos round off cases (backport #30097)
2022-03-08 11:59:02 +05:30
Saqib Ansari
31f20cf144 fix(pos): multiple pos round off cases
(cherry picked from commit 17445c7e04)
2022-03-08 05:55:08 +00:00
Deepesh Garg
ba4d496404 fix: Resolve conflicts 2022-03-08 09:55:36 +05:30
Türker Tunalı
456ebc32f0 fix: translate error message titles 2022-03-08 09:54:33 +05:30
Deepesh Garg
9af14ec0d0 fix: Test cases with discount
(cherry picked from commit d95f8934aa)
2022-03-08 03:39:33 +00:00
Ankush Menat
dfbd0ebf91 fix: apply margin on duplicated doc too
(cherry picked from commit bbc4710fa3)
2022-03-08 03:39:33 +00:00
Deepesh Garg
fd3adbb6fe fix: Item discounts for quotation
(cherry picked from commit 3a547cb0d9)

# Conflicts:
#	erpnext/selling/doctype/quotation/quotation.js
2022-03-08 03:39:33 +00:00
mergify[bot]
a8ae86e23f fix: wrong payment days in salary slip for employees joining/leaving during mid payroll dates (backport #29082) (#30099)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
Co-authored-by: Dany Robert <danyrt@wahni.com>
2022-03-07 20:29:52 +05:30
Deepesh Garg
2b512617fa Merge pull request #30098 from deepeshgarg007/pre_release
chore: Merge branch version-13-hotfix into version-13-pre-release
2022-03-07 19:12:42 +05:30
Deepesh Garg
7452b8572e Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into pre_release 2022-03-07 18:07:42 +05:30
Saqib Ansari
0ed7249484 Merge pull request #30089 from frappe/version-13-pre-release
Merge 'version-13-pre-release' into 'version-13'
2022-03-07 12:16:25 +05:30
Saqib Ansari
25dce9da67 chore: bumped to version 13.21.1 2022-03-07 12:15:42 +05:30
Saqib Ansari
db26928b28 Merge pull request #30086 from frappe/mergify/bp/version-13-hotfix/pr-30084
fix(e-invoicing): remove batch no from e-invoices (backport #30084)
2022-03-07 11:56:58 +05:30
Saqib Ansari
5a5d9b42c0 Merge pull request #30087 from frappe/mergify/bp/version-13-pre-release/pr-30084
fix(e-invoicing): remove batch no from e-invoices (backport #30084)
2022-03-07 11:56:40 +05:30
Saqib Ansari
052ad1fb47 fix(e-invoicing): remove batch no from e-invoices
(cherry picked from commit 031a0dd703)
2022-03-07 06:25:00 +00:00
Saqib Ansari
cf2580083c fix(e-invoicing): remove batch no from e-invoices
(cherry picked from commit 031a0dd703)
2022-03-07 06:24:21 +00:00
Saqib Ansari
58501c7e0e Merge pull request #30085 from frappe/mergify/bp/version-13-pre-release/pr-29991
fix(pos): minor fixes (backport #29991)
2022-03-07 11:53:14 +05:30
Saqib Ansari
3525f0e585 fix: merge conflicts 2022-03-07 11:52:16 +05:30
Saqib Ansari
25c434fb02 fix(pos): coupon code is applied even if ignore pricing rule is check
(cherry picked from commit 81514516f3)

# Conflicts:
#	erpnext/public/js/controllers/transaction.js
2022-03-07 06:20:52 +00:00
Saqib Ansari
da4e43b8bc fix(pos): mode of payment disappears after save
(cherry picked from commit 69c34cd7ae)
2022-03-07 06:20:50 +00:00
Saqib Ansari
8af7a9918b Merge pull request #30079 from frappe/mergify/bp/version-13-hotfix/pr-29939
feat: update ordered qty for packed items (backport #29939)
2022-03-06 13:45:31 +05:30
Saqib Ansari
1e418f9ecc fix: merge conflicts 2022-03-06 13:13:50 +05:30
Saqib Ansari
447692d770 chore: remove unintentional search index
(cherry picked from commit 2f1709dfef)
2022-03-06 07:40:51 +00:00
Saqib Ansari
1c0d0e1ac7 chore: remove unintentional search index
(cherry picked from commit 1e139cf9a1)
2022-03-06 07:40:51 +00:00
Saqib Ansari
14f31ac2fa test: po updates packed item's ordered_qty
(cherry picked from commit 8e3f1e306d)

# Conflicts:
#	erpnext/selling/doctype/sales_order/test_sales_order.py
2022-03-06 07:40:51 +00:00
Saqib Ansari
131158a24c feat: update ordered qty for packed items
(cherry picked from commit 8005fee656)

# Conflicts:
#	erpnext/selling/doctype/sales_order/sales_order.js
2022-03-06 07:40:50 +00:00
Govind S Menokee
363752510e fix: HSN-wise-summary of outward supplies Updated Report
Report changes done in order to meet the specification as per govt guideline - [GUIDELINE](https://taxguru.in/goods-and-service-tax/12-points-note-filing-gstr-1-01st-2021-onwards.html)
2022-03-02 22:29:19 +05:30
Ankush Menat
a8137bda67 fix: dont hardcode precision in routing
Co-Authored-By: Suraj Shetty <surajshetty3416@gmail.com>
(cherry picked from commit 9ef35ef773)
2022-03-02 20:22:02 +05:30
Ankush Menat
d34f5051d7 fix: remove dead dashboard links
(cherry picked from commit f8ac4c082a)
2022-03-02 20:22:02 +05:30
Ankush Menat
5bad8d0e10 fix: dont hardcode precision in routing
Co-Authored-By: Suraj Shetty <surajshetty3416@gmail.com>
(cherry picked from commit 9ef35ef773)
2022-03-02 19:34:01 +05:30
Ankush Menat
478e1eb8db fix: remove dead dashboard links
(cherry picked from commit f8ac4c082a)
2022-03-02 19:34:01 +05:30
mergify[bot]
8ef1f679f7 chore: get stock reco qty from SR instead of SLE (#30059) (#30060)
[skip ci]

(cherry picked from commit 55a966ec41)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-02 14:50:54 +05:30
mergify[bot]
d1cc291c80 fix(Timesheet): fetch exchange rate only if currency is set (backport #30057) (#30058)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-03-02 14:19:11 +05:30
Ankush Menat
95f30ada7b fix: resolve conflicts 2022-03-02 14:08:57 +05:30
Ankush Menat
870799c4e2 fix: repost items with repeating item-warehouses
(cherry picked from commit 3638fbf06b)
2022-03-02 14:08:57 +05:30
Ankush Menat
a1c3e9aef9 revert "fix: FIFO valuation in case of multi-item entries"
This reverts commit b8ee193d1a.

This is huge performance regression for large docs.

(cherry picked from commit 701878f60b)
2022-03-02 14:08:57 +05:30
Ankush Menat
9fc0da1688 test: repack FIFO rates
(cherry picked from commit 2f71c5bcca)
2022-03-02 14:08:57 +05:30
Ankush Menat
bb58c49aef fix: FIFO valuation in case of multi-item entries
(cherry picked from commit ccd2ce56b1)
2022-03-02 14:08:57 +05:30
Ankush Menat
a84e7c633e test: FIFO transfer for multi-batch transaction
(cherry picked from commit b3b7cdfb49)

# Conflicts:
#	erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
2022-03-02 14:08:57 +05:30
Ankush Menat
5be64c2b7f docs: explain make_sl_entries arguments
(cherry picked from commit eb8495a401)
2022-03-02 13:30:20 +05:30
Ankush Menat
5ebfb65b42 fix: ignore serial no during landed cost voucher
(cherry picked from commit efc4b943f8)
2022-03-02 13:30:20 +05:30
Ankush Menat
cdafed1092 test: fix flaky bin value test
(cherry picked from commit 5d85b35f41)
2022-03-02 12:35:26 +05:30
Deepesh Garg
ab69cb7b76 Merge pull request #30014 from frappe/mergify/bp/version-13-hotfix/pr-29978
fix: Commission not applied while making Sales Order from Quotation (backport #29978)
2022-03-01 18:19:10 +05:30
Deepesh Garg
69d3678d21 Merge pull request #30025 from deepeshgarg007/payment_entry_exchange_rate
fix: Exchange rate not getting set in payment entry
2022-03-01 18:18:42 +05:30
mergify[bot]
28a7c79f3c fix: dont validate empty category (#30038)
(cherry picked from commit 65bb727030)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-03-01 17:57:10 +05:30
mergify[bot]
c7539ae416 fix(test): flaky test_point_of_sale (#30035)
(cherry picked from commit 47fe87a72f)

Co-authored-by: Saqib Ansari <nextchamp.saqib@gmail.com>
2022-03-01 17:51:40 +05:30
Saqib Ansari
3eee7412c5 Merge pull request #30033 from frappe/mergify/bp/version-13-hotfix/pr-30032
fix: debit credit difference case with rounding adjustment (backport #30032)
2022-03-01 15:33:10 +05:30
rohitwaghchaure
1e5b9e747f Merge pull request #30023 from frappe/mergify/bp/version-13-hotfix/pr-30015
refactor: removed validation to check zero qty (backport #30015)
2022-03-01 14:44:31 +05:30
Saqib Ansari
d93a41e0d0 fix: debit credit difference case with rounding adjustment
(cherry picked from commit ad2c64f3ff)
2022-03-01 08:43:25 +00:00
Deepesh Garg
40c0f79291 Merge pull request #30027 from frappe/mergify/bp/version-13-hotfix/pr-30026
fix: Deferred revenue booking (backport #30026)
2022-03-01 13:26:28 +05:30
Ankush Menat
5f4965022f fix: track changes on warehouse
(cherry picked from commit d22a1d440c)
2022-03-01 13:11:49 +05:30
Ankush Menat
45ba50036f fix: track changes on warehouse
(cherry picked from commit d22a1d440c)
2022-03-01 13:11:35 +05:30
Deepesh Garg
5f9b2cc1bf fix: Deferred revenue booking
(cherry picked from commit 366120ffee)
2022-03-01 07:12:02 +00:00
Deepesh Garg
86e6bdf2c9 fix: Exchange rate not getting set in payment entry 2022-02-28 21:07:08 +05:30
Deepesh Garg
fb05230d51 Merge pull request #30024 from frappe/mergify/bp/version-13-hotfix/pr-29977
fix: Total taxes and charges in payment entry for multi-currency payments (backport #29977)
2022-02-28 19:36:21 +05:30
Ankush Menat
e89d60147e test: remove transaction commits from buying module
(cherry picked from commit 829c453cb6)
2022-02-28 19:28:36 +05:30
Deepesh Garg
de978ca3d7 test: Add test case for payment entry taxes
(cherry picked from commit 19fb7ead9f)
2022-02-28 13:40:22 +00:00
Deepesh Garg
6ed01fedd4 fix: Total taxes and charges in payment entry for multicurrency payments
(cherry picked from commit b1a46c80d5)
2022-02-28 13:40:22 +00:00
Deepesh Garg
ca6f196bad fix: Remove conflicts 2022-02-28 18:15:29 +05:30
Rohit Waghchaure
b98d3da8e3 fix: removed validation to check zero qty
(cherry picked from commit 39d3f20d9b)
2022-02-28 12:25:20 +00:00
Ankush Menat
aa684e6f3d fix: dont fetch draft/cancelled BOMs
(cherry picked from commit 1d1203d5ec)
2022-02-28 15:15:36 +05:30
Deepesh Garg
ebf70a4b5d test: add correct test case
(cherry picked from commit 4e9a9f35a6)
2022-02-28 07:35:04 +00:00
Deepesh Garg
bce4ca9390 fix: Commission not applied while making Sales Order from Quotation
(cherry picked from commit 0373914704)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
2022-02-28 07:35:04 +00:00
Deepesh Garg
9c0f8fe7fd Merge pull request #30009 from frappe/mergify/bp/version-13-hotfix/pr-29928
fix: Account filter in PSOA (backport #29928)
2022-02-28 12:57:04 +05:30
Marica
5aa42c736f Merge pull request #29994 from marination/fix-user-unique-cart-hotfix
fix(e-commerce): Unique Shopping Cart Per Logged In User
2022-02-28 12:37:39 +05:30
Marica
a531792ea5 Merge branch 'version-13-hotfix' into fix-user-unique-cart-hotfix 2022-02-28 12:13:57 +05:30
Deepesh Garg
c9da4e1059 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-29865 2022-02-28 11:43:01 +05:30
Deepesh Garg
e509ce5438 fix: Account filter in PSOA
(cherry picked from commit 70b960e650)
2022-02-28 06:12:42 +00:00
Saqib Ansari
f21951fa33 Merge pull request #29992 from frappe/mergify/bp/version-13-hotfix/pr-29991
fix(pos): minor fixes (backport #29991)
2022-02-28 11:34:27 +05:30
mergify[bot]
5d69a182f6 fix(ux): make "allow zero valuation rate" readonly if "s_warehouse" is set (#29681) (#30002)
* chore: make allow zero valuation rate readonly if s_warehouse is set

* fix: setting the checkbox to zero whenever the source warehouse is set

* fix: remove allow_on_submit and refresh trigger

Co-authored-by: Ankush Menat <ankush@frappe.io>
(cherry picked from commit 77ffcd3aed)

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-02-26 12:12:10 +05:30
Sagar Sharma
067ede76ea fix: validate Work Order qty against Production Plan (#29721)
* fix: validate Work Order qty against Production Plan

* chore: err msg when max_qty is 0

* test: add test for overproduction

* fix: CI
2022-02-26 11:13:00 +05:30
mergify[bot]
afade046fb fix: org chart connectors not rendered when Employee Naming is set to Full Name (backport #29997) (#29998)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-02-25 23:07:04 +05:30
Devin Slauenwhite
03945aae37 fix: get cart items for logged in user. 2022-02-25 16:43:21 +05:30
Devin Slauenwhite
78c2a369c4 fix: get cart for logged in user. 2022-02-25 16:43:12 +05:30
Devin Slauenwhite
a067aabb3c test: many users linked to customer shopping cart 2022-02-25 16:43:04 +05:30
Marica
c099833560 Merge pull request #29970 from frappe/mergify/bp/version-13-hotfix/pr-29913
fix: Block merging items if both have product bundles (backport #29913)
2022-02-25 16:10:09 +05:30
Saqib Ansari
6bb1dccddd fix: merge conflicts 2022-02-25 15:45:52 +05:30
Saqib Ansari
f2264fa58d fix(pos): coupon code is applied even if ignore pricing rule is check
(cherry picked from commit 81514516f3)

# Conflicts:
#	erpnext/public/js/controllers/transaction.js
2022-02-25 10:06:21 +00:00
Saqib Ansari
dcef0de906 fix(pos): mode of payment disappears after save
(cherry picked from commit 69c34cd7ae)
2022-02-25 10:06:21 +00:00
Deepesh Garg
5ec6a96882 Merge pull request #29960 from frappe/mergify/bp/version-13-hotfix/pr-29879
fix: Validate party account with company (backport #29879)
2022-02-24 00:00:30 +05:30
Ankush Menat
82ed96859a Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-29913 2022-02-23 17:21:25 +05:30
mergify[bot]
95bf7acff8 test: fix flaky stateful tests (#29749) (#29969)
Co-authored-by: Ankush Menat <ankush@frappe.io>
Co-Authored-By: Marica <maricadsouza221197@gmail.com>
2022-02-23 17:07:13 +05:30
marination
34e0b85d6a fix: Check if both old and new items have bundles before merging
- If only one has bundle against it, they can be merged

(cherry picked from commit a33f04ea41)
2022-02-23 11:30:04 +00:00
marination
e0ed91433b test: Item Merging with Product Bundles
(cherry picked from commit 530f9f7029)
2022-02-23 11:30:04 +00:00
marination
5d1b7ec61d fix: Block merging items if both have product bundles
(cherry picked from commit 28cc2dbb72)
2022-02-23 11:30:04 +00:00
mergify[bot]
cdcd60a1ea chore: Rollback after each test, due to premature commit via remove_user_permission (backport #29964) (#29967)
* chore: Rollback after each test, due to premature commit via `remove_user_permission`

- `remove_user_permission` in `test_warehouse_user` calls delete_doc that enqueues dynamic link deletion
- Execution of background job eventually commits
- While in the test suite it runs sequentially in the same thread and commits whatever was done until then
- Which is why the rollback in `tearDownClass` is quite useless here
- This premature commit causes many illegal transactions caught by `assertRaises` to be committed in the db
- This creates faulty/dirty ledgers and breaks reports, as outiside the test suite this shouldn't/wouldn't happen
- Rollback after each test, and for `test_warehouse_user` in particular, manually cancel transaction

(cherry picked from commit bf8743713d)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/test_stock_entry.py

* test: Make Variant if absent in `test_variant_work_order`, keep test atomic

(cherry picked from commit 5ff3705872)

* fix: Merge conflicts

Co-authored-by: marination <maricadsouza221197@gmail.com>
2022-02-23 16:09:37 +05:30
Ankush Menat
37ba3666ac fix: reset stock value if no qty 2022-02-23 14:03:07 +05:30
Ankush Menat
6d354edb6b refactor: code duplication for fallback rates 2022-02-23 14:03:07 +05:30
Ankush Menat
982476d684 chore: batch flag and consumption rate in invariant report 2022-02-23 14:03:07 +05:30
Deepesh Garg
da1713ad00 fix: Remove unintended changes
(cherry picked from commit a61790c00f)
2022-02-23 05:52:53 +00:00
Deepesh Garg
e82f55ce9e fix: Validate party account with company
(cherry picked from commit 5a2b571aa9)
2022-02-23 05:52:52 +00:00
Deepesh Garg
c040d7df8a Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-29740 2022-02-23 11:10:54 +05:30
Deepesh Garg
d1049b0d00 fix: Resolve conflicts 2022-02-23 11:10:07 +05:30
Suraj Shetty
1fbf2b511f Merge pull request #29958 from frappe/mergify/bp/version-13-hotfix/pr-29956 2022-02-23 11:01:38 +05:30
Suraj Shetty
cebe8888af fix: Email translations
(cherry picked from commit aaa84a21ba)
2022-02-23 05:31:15 +00:00
Saqib Ansari
1689409438 Merge pull request #29714 from GangaManoj/fetch-valuation-rate
fix: Fetch valuation rate for stock items consumed during asset repair
2022-02-23 09:24:27 +05:30
Deepesh Garg
259a091a37 fix: Remove print statements
(cherry picked from commit a4c6cb9f12)
2022-02-22 15:14:01 +00:00
Deepesh Garg
247325e4f5 fix: Update bank reconciliation statement
(cherry picked from commit 0b5e618e3a)
2022-02-22 15:14:01 +00:00
Deepesh Garg
e998521ca6 fix: Update queries in Bank Reconciliation Tool
(cherry picked from commit 295cbb0ff2)
2022-02-22 15:14:00 +00:00
Deepesh Garg
c2129b762a fix: Add patch for account fields
(cherry picked from commit a0bdcbd0cd)

# Conflicts:
#	erpnext/patches.txt
2022-02-22 15:13:59 +00:00
Deepesh Garg
93033fb87f feat: Bank Reconciliation for loan documents
(cherry picked from commit 555b1335f6)
2022-02-22 15:13:58 +00:00
Deepesh Garg
ee84985dc7 Merge pull request #29947 from frappe/mergify/bp/version-13-hotfix/pr-29942
fix: Taxjar minor fixes (backport #29942)
2022-02-22 19:39:39 +05:30
Subin Tom
46ed9a059c fix: Taxjar minor fixes
(cherry picked from commit 1682a26fe6)
2022-02-22 14:08:52 +00:00
Saqib Ansari
945a685a34 Merge pull request #29937 from frappe/mergify/bp/version-13-hotfix/pr-29900
fix(asset): no. of depr booked cannot be equal to total no. of depr (backport #29900)
2022-02-22 18:45:01 +05:30
Saqib Ansari
f4d987c21e Merge branch 'version-13-hotfix' into fetch-valuation-rate 2022-02-22 18:44:46 +05:30
mergify[bot]
807997c6ec fix: remove customer field value when MR is not customer provided (#29938) (#29941)
(cherry picked from commit 7f55226a58)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-02-22 18:20:59 +05:30
Saqib Ansari
b6d42ff571 test: number_of_depr_booked = total_number_of_depr
(cherry picked from commit 780694f6e2)
2022-02-22 08:54:16 +00:00
Saqib Ansari
2e6a076f90 fix(asset): no. of depreciation booked cannot be equal to total no. of depreciations
(cherry picked from commit c5808543c8)
2022-02-22 08:54:16 +00:00
Saqib Ansari
7157a86ea0 Merge branch 'version-13-hotfix' into fetch-valuation-rate 2022-02-22 14:23:29 +05:30
mergify[bot]
6539d32ca9 fix: Fetch conversion factor even if it already existed in row, on item change (#29917) (#29935)
* fix: Fetch conversion factor even if it already existed in row, on item change

* fix: Retain manually changed conversion factor

- If item code changes, reset conversion factor on client side
- Keep API behavious consistent, if conversion factor is sent, same must come back
- API should not ideally reset values in most cases

(cherry picked from commit 235fc127b3)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2022-02-22 13:01:31 +05:30
mergify[bot]
0f101b5209 fix(Salary Slip): TypeError while clearing any amount field in components (backport #29931) (#29932)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-02-22 11:56:56 +05:30
mergify[bot]
233a75a438 fix(LMS): program enrollment does not give any feedback (backport #29922) (#29924)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-02-21 23:10:20 +05:30
Rohit Waghchaure
f87ba6ac0a Merge branch 'version-13-pre-release' into version-13 2022-02-21 20:29:03 +05:30
Rohit Waghchaure
506e1221b8 bumped to version 13.21.0 2022-02-21 20:49:03 +05:50
rohitwaghchaure
a6a7504d6c Merge pull request #29920 from rohitwaghchaure/change-log-for-v13-21
chore: change log for v13.21.0
2022-02-21 20:21:27 +05:30
Rohit Waghchaure
b05d2201eb chore: change log for v13.21.0 2022-02-21 20:18:04 +05:30
mergify[bot]
6b87d38a80 fix: JobCard TimeLog to_date (#29872) (#29919)
(cherry picked from commit e4c4dc402e)

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-02-21 20:17:05 +05:30
Deepesh Garg
a63373ce98 fix: Gross profit for credit notes
(cherry picked from commit 973f6b1bbd)
2022-02-21 13:59:15 +00:00
Deepesh Garg
9a0b83027a fix: Remove unused param
(cherry picked from commit 07bcbc6c7e)
2022-02-21 13:59:15 +00:00
Deepesh Garg
b0a9ff0b04 fix: Update columns in new format
(cherry picked from commit 2172ab2d37)
2022-02-21 13:59:15 +00:00
Deepesh Garg
ab4a96dadd fix: Multiple fixes in Gross Profit report
(cherry picked from commit da73685f71)
2022-02-21 13:59:15 +00:00
Saqib Ansari
f107a6f3f2 Merge branch 'version-13-hotfix' into fetch-valuation-rate 2022-02-21 17:56:11 +05:30
Ankush Menat
9adbaaea4a fix: round off increments in numeric item variant
(cherry picked from commit 00e8565868)
2022-02-21 17:44:41 +05:30
Deepesh Garg
b10d167c98 Merge pull request #29910 from frappe/mergify/bp/version-13-pre-release/pr-29907
fix: Total Credit amount in TDS Payable monthly report (backport #29907)
2022-02-21 15:40:53 +05:30
Deepesh Garg
e4281734fe Merge pull request #29909 from frappe/mergify/bp/version-13-hotfix/pr-29907
fix: Total Credit amount in TDS Payable monthly report (backport #29907)
2022-02-21 15:40:41 +05:30
Deepesh Garg
a5fbf89285 fix: Total Credit amount in TDS Payable monthly report
(cherry picked from commit a82cf7214e)
2022-02-21 10:05:49 +00:00
Deepesh Garg
a6fba24ab1 fix: Total Credit amount in TDS Payable monthly report
(cherry picked from commit a82cf7214e)
2022-02-21 10:05:23 +00:00
Saqib Ansari
1b0ee018b5 Merge branch 'version-13-hotfix' into fetch-valuation-rate 2022-02-21 15:11:26 +05:30
Deepesh Garg
15849c5cac Merge pull request #29906 from frappe/mergify/bp/version-13-pre-release/pr-29887
fix: Ledger entries on LIA for term loans (backport #29887)
2022-02-21 14:54:18 +05:30
Deepesh Garg
e70933eea2 Merge pull request #29905 from frappe/mergify/bp/version-13-hotfix/pr-29887
fix: Ledger entries on LIA for term loans (backport #29887)
2022-02-21 14:54:05 +05:30
Marica
28abe13e0b Merge pull request #29908 from frappe/mergify/bp/version-13-pre-release/pr-29904
chore: Show 'Produced Qty' field in Sales Order Item (backport #29903) (backport #29904)
2022-02-21 14:42:22 +05:30
mergify[bot]
251288994c chore: Show 'Produced Qty' field in Sales Order Item (#29903) (#29904)
(cherry picked from commit e952cce17d)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
(cherry picked from commit 14c5be7c27)
2022-02-21 09:06:01 +00:00
Saqib Ansari
bc28755978 fix: sider 2022-02-21 14:26:21 +05:30
Deepesh Garg
a015fcc921 fix: Ledger entries on LIA for term loans
(cherry picked from commit 1aa12fb3f1)
2022-02-21 08:55:36 +00:00
Deepesh Garg
130f039549 fix: Ledger entries on LIA for term loans
(cherry picked from commit 1aa12fb3f1)
2022-02-21 08:54:58 +00:00
mergify[bot]
14c5be7c27 chore: Show 'Produced Qty' field in Sales Order Item (#29903) (#29904)
(cherry picked from commit e952cce17d)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2022-02-21 14:23:52 +05:30
Saqib Ansari
2aba6c38b3 fix: cannot edit valutaion_rate in asset repair 2022-02-21 14:01:12 +05:30
Saqib Ansari
57d5a027fb fix: remove redundant method 2022-02-21 13:58:01 +05:30
Saqib Ansari
c34fcf3083 Merge pull request #29899 from frappe/mergify/bp/version-13-pre-release/pr-29896
fix(pos): removal of coupon code (backport #29896)
2022-02-21 12:02:49 +05:30
Saqib Ansari
2c84b1dfa6 Merge pull request #29898 from frappe/mergify/bp/version-13-hotfix/pr-29896
fix(pos): removal of coupon code (backport #29896)
2022-02-21 11:52:31 +05:30
Saqib Ansari
9b241cb6f1 fix(pos): removal of coupon code
(cherry picked from commit fa38c291bd)
2022-02-21 06:19:46 +00:00
Saqib Ansari
8d24cb44a8 fix(pos): removal of coupon code
(cherry picked from commit fa38c291bd)
2022-02-21 06:19:24 +00:00
rohitwaghchaure
85b7bc8d6b Merge pull request #29892 from frappe/mergify/bp/version-13-hotfix/pr-29771
fix: Error in consolidated financial statements (backport #29771)
2022-02-20 19:54:44 +05:30
Deepesh Garg
232ba3580b fix: Linting issues
(cherry picked from commit fec40aac7a)
2022-02-20 13:48:45 +00:00
Deepesh Garg
b99a95b280 fix: Remove commented out code
(cherry picked from commit 42cdd6d237)
2022-02-20 13:48:45 +00:00
Deepesh Garg
02b7541fb9 fix: Error in consolidated financial statements
(cherry picked from commit ae613008be)
2022-02-20 13:48:45 +00:00
Deepesh Garg
ce2a5c97eb Merge pull request #29888 from frappe/mergify/bp/version-13-hotfix/pr-29869
fix: GSTIN filter for GSTR-1 report (backport #29869)
2022-02-20 17:25:10 +05:30
Deepesh Garg
6084aeb7d1 chore: remove console statements
(cherry picked from commit d188fcc066)
2022-02-19 14:12:43 +00:00
Deepesh Garg
3a98cc351c Update gstr_1.js
(cherry picked from commit a28ec89507)
2022-02-19 14:12:43 +00:00
Deepesh Garg
41b82145ea fix: GSTIN filter for GSTR-1 report
(cherry picked from commit 87b074ac09)
2022-02-19 14:12:42 +00:00
Marica
920a99ee76 Merge pull request #29877 from frappe/mergify/bp/version-13-pre-release/pr-29816
fix: Stock Ageing Transfer Bucket logic for Repack Entry with split batch rows (backport #29816)
2022-02-18 20:02:17 +05:30
Marica
8dc6468b6a Merge pull request #29878 from frappe/mergify/bp/version-13-pre-release/pr-29825
fix: Update SO via Work Order made from MR (attached to SO) (backport #29803) (backport #29825)
2022-02-18 20:01:18 +05:30
Marica
52aedcf8c4 Merge pull request #29876 from frappe/mergify/bp/version-13-hotfix/pr-29816
fix: Stock Ageing Transfer Bucket logic for Repack Entry with split batch rows (backport #29816)
2022-02-18 19:34:35 +05:30
Marica
104d3e1019 fix: Solve merge conflict in patches.txt 2022-02-18 19:27:11 +05:30
marination
6cf1049495 chore: Move patch that updates SO from WO to v13
(cherry picked from commit 94a1016623)

# Conflicts:
#	erpnext/patches.txt
2022-02-18 13:51:12 +00:00
marination
b46bd0d9d1 chore: Patch to update SO work_order_qty and Linter fix
(cherry picked from commit 0ca58d7627)

# Conflicts:
#	erpnext/patches.txt
(cherry picked from commit 98ce6a1c8d)

# Conflicts:
#	erpnext/patches.txt
2022-02-18 13:51:12 +00:00
marination
0e3398c761 test: SO > MR > WO flow
(cherry picked from commit f9d52e7346)
(cherry picked from commit 2ce07eff71)
2022-02-18 13:51:11 +00:00
marination
8dbe953b8f fix: Update SO via Work Order made from MR (attached to SO)
- Add SO Item reference in WO from MR (that was made from SO)

(cherry picked from commit 18731622c4)
(cherry picked from commit bb119c8e52)
2022-02-18 13:51:10 +00:00
marination
113222628b test: Negative Stock, over consumption & over production with split rows, balance precision
(cherry picked from commit d5be536740)
2022-02-18 13:50:36 +00:00
marination
ae7aead4d0 fix: Range Qty precision
(cherry picked from commit ed4a6c6cc6)
2022-02-18 13:50:36 +00:00
marination
4126e42ae7 fix: Precision of available qty and negative stock in transfer bucket
- Maintain only positive values in transfer bucket
- Use it to neutralize/add stock to fifo queue

(cherry picked from commit d3fbbcfed3)
2022-02-18 13:50:36 +00:00
marination
51af328a53 chore: Add transfer bucket working to .md file
(cherry picked from commit f6233e77c6)
2022-02-18 13:50:35 +00:00
marination
06758e53fd test: Stock Ageing FIFO buckets for Repack entry with same item
(cherry picked from commit ea3b7de867)
2022-02-18 13:50:35 +00:00
marination
cb4e6b9221 fix: Transfer Bucket logic for Repack Entry with split batch rows
(cherry picked from commit 799671c748)
2022-02-18 13:50:35 +00:00
Marica
f958956322 Merge pull request #29825 from frappe/mergify/bp/version-13-hotfix/pr-29803
fix: Update SO via Work Order made from MR (attached to SO) (backport #29803)
2022-02-18 19:20:12 +05:30
marination
88ca371887 test: Negative Stock, over consumption & over production with split rows, balance precision
(cherry picked from commit d5be536740)
2022-02-18 13:49:09 +00:00
marination
fde5a9e6f7 fix: Range Qty precision
(cherry picked from commit ed4a6c6cc6)
2022-02-18 13:49:09 +00:00
marination
c66b5752e2 fix: Precision of available qty and negative stock in transfer bucket
- Maintain only positive values in transfer bucket
- Use it to neutralize/add stock to fifo queue

(cherry picked from commit d3fbbcfed3)
2022-02-18 13:49:08 +00:00
marination
cab05dbefb chore: Add transfer bucket working to .md file
(cherry picked from commit f6233e77c6)
2022-02-18 13:49:08 +00:00
marination
690ae52a8e test: Stock Ageing FIFO buckets for Repack entry with same item
(cherry picked from commit ea3b7de867)
2022-02-18 13:49:08 +00:00
marination
d86e63e22d fix: Transfer Bucket logic for Repack Entry with split batch rows
(cherry picked from commit 799671c748)
2022-02-18 13:49:07 +00:00
Rohit Waghchaure
58aedaa0f7 fix: added item name in the excel sheet
(cherry picked from commit 02e77029fa)
2022-02-17 20:18:01 +05:30
Rohit Waghchaure
72dec4b28f fix: added item name in the excel sheet
(cherry picked from commit 02e77029fa)
2022-02-17 20:17:47 +05:30
Ankush Menat
90ebe6f266 fix: avoid creating bins without item-wh
Co-Authored-By:  Shadrak Gurupnor <30501401+shadrak98@users.noreply.github.com>
Co-Authored-By: Saurabh <saurabh6790@gmail.com>
(cherry picked from commit c36bd7e1a6)
2022-02-17 20:17:13 +05:30
Ankush Menat
29a1f338a4 fix: avoid creating bins without item-wh
Co-Authored-By:  Shadrak Gurupnor <30501401+shadrak98@users.noreply.github.com>
Co-Authored-By: Saurabh <saurabh6790@gmail.com>
(cherry picked from commit c36bd7e1a6)
2022-02-17 20:16:57 +05:30
Deepesh Garg
bdef164061 Merge pull request #29827 from frappe/mergify/bp/version-13-hotfix/pr-29137
feat: Payment Terms Status report (backport #29137)
2022-02-17 19:16:28 +05:30
Rucha Mahabal
ddfdb445fa chore: remove unused code and fields related to workstation from Timesheet Detail
(cherry picked from commit bef46e2b64)
2022-02-17 18:22:43 +05:30
Rucha Mahabal
98bafc5898 test: timesheet not overlapping with continuous timelogs
(cherry picked from commit 47ff968253)
2022-02-17 18:22:43 +05:30
Rucha Mahabal
91e6fea2cd fix: convert overlap raw query to frappe.qb
(cherry picked from commit 3ec9acf8f7)
2022-02-17 18:22:43 +05:30
Rucha Mahabal
6db0ea8be8 fix(Timesheet): convert time logs to datetime while checking for overlap
(cherry picked from commit e2e998fbd9)
2022-02-17 18:22:43 +05:30
Saqib Ansari
f9282d2a6b Merge pull request #29861 from frappe/mergify/bp/version-13-pre-release/pr-29859
fix: coupon code is applied even if ignore_pricing_rule is enabled (backport #29859)
2022-02-17 16:22:05 +05:30
Saqib Ansari
5db80b2b57 Merge pull request #29860 from frappe/mergify/bp/version-13-hotfix/pr-29859
fix: coupon code is applied even if ignore_pricing_rule is enabled (backport #29859)
2022-02-17 16:20:51 +05:30
Saqib Ansari
381ccd7653 fix: coupon code is applied even if ignore_pricing_rule is enabled
(cherry picked from commit 2743999785)
2022-02-17 10:42:43 +00:00
Saqib Ansari
63079f3e07 fix: coupon code is applied even if ignore_pricing_rule is enabled
(cherry picked from commit 2743999785)
2022-02-17 10:42:15 +00:00
Saqib Ansari
6559c1cd01 Merge pull request #29851 from frappe/mergify/bp/version-13-pre-release/pr-29848
fix: currency in bank reconciliation tool (backport #29848)
2022-02-17 14:40:22 +05:30
Saqib Ansari
d898cb45dc fix: currency in bank reconciliation tool
(cherry picked from commit 60674e52b8)
2022-02-17 08:57:47 +00:00
marination
94a1016623 chore: Move patch that updates SO from WO to v13 2022-02-16 17:45:41 +05:30
Marica
8727addec3 Merge pull request #29824 from frappe/mergify/bp/version-13-pre-release/pr-29823
perf: Weed out disabled variants via sql query instead of pythonic looping separately (backport #29639) (backport #29823)
2022-02-16 14:30:47 +05:30
ruthra kumar
2ca25fc9f1 test: added test for alternate currency
- Sales Order and Invoice will be submitted in USD with exchange rate
of 70 with the default company currency
- Report will display in defauly company currency

(cherry picked from commit 48f37c7659)
2022-02-16 08:46:23 +00:00
ruthra kumar
fa03246a94 test: refactor and fix failing test case
(cherry picked from commit 49fdc6c52e)
2022-02-16 08:46:23 +00:00
ruthra kumar
07c094152e refactor: create invoices list without if else
(cherry picked from commit a4b8d67323)
2022-02-16 08:46:22 +00:00
ruthra kumar
63c73e6eaf fix: default to company currency in report output
(cherry picked from commit 85ed0fb8d6)
2022-02-16 08:46:22 +00:00
ruthra kumar
2614de9ab3 refactor: currency field and code cleanup
(cherry picked from commit bc244d0740)
2022-02-16 08:46:22 +00:00
Deepesh Garg
b249478fcc fix: Copyright info
(cherry picked from commit 4284017e9d)
2022-02-16 08:46:21 +00:00
ruthra
b6910912a9 test: qty and rate changed to remove need for fractional Nos
(cherry picked from commit 4535a7a301)
2022-02-16 08:46:21 +00:00
ruthra
c6fb52a31a refactor: remove unused imports
(cherry picked from commit edd980acdc)
2022-02-16 08:46:21 +00:00
ruthra
408704e269 test: fix failing test case payment terms status
(cherry picked from commit 9f1e68801d)
2022-02-16 08:46:20 +00:00
ruthra
bc2b4411ee feat: Payment Terms Status report
- calculate status at runtime for payment terms based on invoices
 - invoices are used in FIFO method

(cherry picked from commit 1bac793083)
2022-02-16 08:46:20 +00:00
marination
98ce6a1c8d chore: Patch to update SO work_order_qty and Linter fix
(cherry picked from commit 0ca58d7627)

# Conflicts:
#	erpnext/patches.txt
2022-02-16 08:35:11 +00:00
marination
2ce07eff71 test: SO > MR > WO flow
(cherry picked from commit f9d52e7346)
2022-02-16 08:35:09 +00:00
marination
bb119c8e52 fix: Update SO via Work Order made from MR (attached to SO)
- Add SO Item reference in WO from MR (that was made from SO)

(cherry picked from commit 18731622c4)
2022-02-16 08:35:09 +00:00
marination
ad0d865ccc chore: Remove commented out code
(cherry picked from commit 29c576e144)
(cherry picked from commit de834a8362)
2022-02-16 08:33:44 +00:00
marination
abd5d3caea chore: Fix flaky test test_exact_match_with_price
- Clear cart settings in cache to avoid stale values

(cherry picked from commit 4f5a0b8941)
(cherry picked from commit b75e982286)
2022-02-16 08:33:44 +00:00
marination
2bcc77583a fix: Trim spaces from attributes (multi-variant creation) & explicit method for building cache
- Multiple Item Variants creation fails due to extra spaces in attributes from popup. Clean them before passing to server side
- Mention explicit method to build variants cache to avoid ambiguity between old method path (pre-refactor)

(cherry picked from commit a64228741d)
(cherry picked from commit ca8986028d)
2022-02-16 08:33:43 +00:00
marination
585b760144 perf: Weed out disabled variants via sql query instead of pythonic looping separately
- If the number of variants are large (almost 2lakhs), the query to get variants and attribute data takes time
- If the no.of disabled attributes is large as well, the list comprehension weeding out disabled variants takes forever
- We dont need to loop over the variants data so many times
- Avoid any `if a in list(b)` is best when the iterables have tremendous data

(cherry picked from commit 26bd3053d1)
(cherry picked from commit 251656ab52)
2022-02-16 08:33:43 +00:00
rohitwaghchaure
c843b0b210 Merge pull request #29809 from rohitwaghchaure/merge-branch-hotfix-to-pre-release-for-13-21
chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release'
2022-02-15 17:54:17 +05:30
mergify[bot]
20ba40066a fix: dont attempt to set batch number if item doesn't have batch no (#29812) (#29814)
This causes other triggers and unnecessary changes (e.g. price list)

(cherry picked from commit f89a64db48)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-02-15 16:12:17 +05:30
Deepesh Garg
335e80f787 Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into merge-branch-hotfix-to-pre-release-for-13-21 2022-02-15 15:56:33 +05:30
Rohit Waghchaure
01356031bb chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release' 2022-02-15 15:14:10 +05:30
Deepesh Garg
540559d6fd Merge branch 'version-13-pre-release' into version-13 2022-02-11 19:14:54 +05:30
Deepesh Garg
5df93916b4 bumped to version 13.20.1 2022-02-11 19:34:54 +05:50
Deepesh Garg
6cb5e85342 Merge pull request #29758 from frappe/mergify/bp/version-13-pre-release/pr-29756
Revert "fix(India): Tax calculation for overseas suppliers" (backport #29756)
2022-02-11 18:06:03 +05:30
Deepesh Garg
5855225366 Revert "fix(India): Tax calculation for overseas suppliers"
(cherry picked from commit ea20c63182)
2022-02-11 12:11:31 +00:00
Ankush Menat
70951bd833 ci: bump ci python version for patch test 2022-02-10 10:57:24 +05:30
Rohit Waghchaure
9dec4a2454 Merge branch 'version-13-pre-release' into version-13 2022-02-09 14:44:10 +05:30
Rohit Waghchaure
7e910f21b8 bumped to version 13.20.0 2022-02-09 15:04:10 +05:50
rohitwaghchaure
b27c4ac6e2 Merge pull request #29727 from rohitwaghchaure/change-log-for-v13-20
chore: change log for v13.20.0
2022-02-09 14:42:15 +05:30
Rohit Waghchaure
a451c49a42 chore: change log for v13.20.0 2022-02-09 14:40:07 +05:30
GangaManoj
e1a9b61103 fix: Pass value instead of array 2022-02-08 17:59:06 +05:30
GangaManoj
df71907be4 fix: Fetch valuation rate 2022-02-08 17:37:38 +05:30
Deepesh Garg
427d8c865a Merge pull request #29678 from frappe/mergify/bp/version-13-pre-release/pr-29460
feat: Refund entry against loans (backport #29460)
2022-02-07 19:17:07 +05:30
Deepesh Garg
5ed44bdb88 Merge pull request #29686 from frappe/mergify/bp/version-13-pre-release/pr-29660
fix: Add missing key in Loan (backport #29660)
2022-02-07 19:05:06 +05:30
Deepesh Garg
eb36ebee8f fix: Resolve conflicts 2022-02-07 18:49:57 +05:30
Deepesh Garg
159e492053 test: Add test case for repayment against partially disbursed loans
(cherry picked from commit ef69d1fd38)
2022-02-07 13:16:19 +00:00
Abhinav Raut
6dbb3cdf75 fix: missing key in loan
(cherry picked from commit d0043bdbac)
2022-02-07 13:16:19 +00:00
Deepesh Garg
25012399fa fix: Add disbursement accounts to tests
(cherry picked from commit 8ece2845f2)
2022-02-07 09:09:20 +00:00
Deepesh Garg
4ab6994b3d feat: Refund entry against loans
(cherry picked from commit c68c70f8bc)

# Conflicts:
#	erpnext/loan_management/doctype/loan/loan.json
#	erpnext/patches.txt
2022-02-07 09:09:19 +00:00
Ankush Menat
e618571fb3 fix: dont show cancelled PO items in plan report
(cherry picked from commit 6459ceaea1)
2022-02-06 20:07:22 +05:30
Marica
c99a14a7f3 Merge pull request #29646 from frappe/mergify/bp/version-13-pre-release/pr-29642
fix: Regenerate packed items on newly mapped doc (backport #29642)
2022-02-04 23:25:17 +05:30
marination
db24fca8c1 fix: Regenerate packed items on newly mapped doc
- Cannot determine action on newly mapped DN that hasnt been inserted
- Rows could have been deleted, updated, added, etc. before first save
- In this case , reset packing table

(cherry picked from commit bd41a99c8a)
2022-02-04 17:33:17 +00:00
Marica
e095e27cb9 Merge pull request #29629 from frappe/mergify/bp/version-13-pre-release/pr-29456
fix: Incorrect packing list for recurring items & code cleanup (backport #29456)
2022-02-04 10:17:46 +05:30
marination
bdfb478b3b fix: (Linter) import sequence
(cherry picked from commit f18ec2d947)
2022-02-03 16:41:20 +00:00
marination
2428812dcc test: Packed Items test file
(cherry picked from commit 4e7b4dc9a8)
2022-02-03 16:41:20 +00:00
marination
677a3975ba refactor: Price fetching and updation logic
- fetch price from price list, use item master valuation rate as fallback fo0r packed item
- use a item code, item row name map to maintain cumulative price
- reset table if item in a row is replaced
- loop over items table only to set price, lesser iterations than packed items table

(cherry picked from commit 2f4d266ee1)
2022-02-03 16:41:19 +00:00
marination
5d37c9c38d chore: Break updation logic into smaller functions
- Smaller functions for updation
- All calls visible from parent function to avoid context switching due to nested calls

(cherry picked from commit 2c14ab0439)
2022-02-03 16:41:19 +00:00
marination
b836d4fe25 fix: Linter and minor code refactor
- Create an indexed map of stale packed items table to avoid loops to check if packed item row exists
- Reset packed items if row deletion takes place
- Renamed functions to self-explain them
- Split long function
- Reduce function calls inside function (makes it harder to follow through)

(cherry picked from commit 4c677eafe9)
2022-02-03 16:41:18 +00:00
marination
153fd355bc chore: SQL to QB & accomodate Update Items
- `doc_before_save` does not exist via Update Items (updates stuff in the backend so doc isn't considered unsaved/dirty)
- converted more raw sql to qb and ORM

(cherry picked from commit f8a5786542)
2022-02-03 16:41:18 +00:00
marination
350cac9207 fix: Incorrect packing list for recurring items & code cleanup
- Fix Incorrect packing list for recurring items in the Items table
- Re-organised functions based on external use and order of use
- Deleted `clean_packing_list` function and reduced no.of loops
- Raw SQL to QB
- Minor formatting changes

(cherry picked from commit 3f48fc1898)
2022-02-03 16:41:17 +00:00
mergify[bot]
c054d1f7ee fix: ignore empty customer/supplier in item query (#29610) (#29614)
* fix: dont try to filter by customer/supplier if None

* test: item query with emtpy supplier

(cherry picked from commit 41a95e5624)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-02-03 14:44:33 +05:30
Deepesh Garg
68b337316a Merge pull request #29573 from frappe/mergify/bp/version-13-pre-release/pr-29561
feat: Provisional accounting for expenses (backport #29451) (backport #29561)
2022-02-01 20:39:09 +05:30
Deepesh Garg
1ab3714fd0 chore: Resolve conflicts 2022-02-01 19:41:00 +05:30
Deepesh Garg
dbb3ea3a6e chore: fix tests
(cherry picked from commit 640b2d57de)
2022-02-01 12:42:09 +00:00
Deepesh Garg
abfccde664 fix: Linting Issues
(cherry picked from commit c71dbda166)
2022-02-01 12:42:09 +00:00
Deepesh Garg
293ec00023 chore: Resolve conflicts
(cherry picked from commit aa16a4bb5f)

# Conflicts:
#	erpnext/patches.txt
2022-02-01 12:42:09 +00:00
Deepesh Garg
061a569f4d feat: Provisional accounting for expenses (#29451)
* feat: Provisonal accounting for expenses

* fix: Method for provisional accounting entry

* chore: Add test case

* fix: Remove test case

* fix: Use company doctype

* fix: Add provisional expense account field in Purchase Receipt Item

* fix: Test case

* fix: Move provisional expense account to parent

* fix: Patch

(cherry picked from commit 528c71382f)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
#	erpnext/patches.txt
#	erpnext/setup/doctype/company/company.json
#	erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
(cherry picked from commit a54c84728f)

# Conflicts:
#	erpnext/patches.txt
2022-02-01 12:42:08 +00:00
rohitwaghchaure
8c0613a600 Merge pull request #29542 from rohitwaghchaure/merge-branch-hotfix-to-pre-release-for-13-20
chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release'
2022-01-31 20:16:11 +05:30
Rohit Waghchaure
862961d92e chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release' 2022-01-31 19:38:44 +05:30
Rohit Waghchaure
5c8fc3890f Merge branch 'version-13-pre-release' into version-13 2022-01-23 14:57:36 +05:30
Rohit Waghchaure
f44d90e097 bumped to version 13.19.0 2022-01-23 15:17:36 +05:50
rohitwaghchaure
4305d58273 Merge pull request #29415 from rohitwaghchaure/change-log-for-v13-19
chore: change log for v13.19.0
2022-01-23 14:55:32 +05:30
Rohit Waghchaure
194b7fbf2f chore: change log for v13.19.0 2022-01-23 14:50:49 +05:30
Marica
f9de807b25 Merge pull request #29400 from marination/minor-quotation-doc-wrong-var-pre-release
fix: Wrong variable name breaks Cart Items rendering
2022-01-21 19:45:49 +05:30
marination
5ed4b43b73 debug: CI 2022-01-21 19:21:38 +05:30
marination
b27fe43920 fix: Wrong variable name breaks Cart Items rendering 2022-01-21 16:04:48 +05:30
Marica
398fa6eb48 Merge pull request #29396 from frappe/mergify/bp/version-13-pre-release/pr-29383
fix: Cart & Popup Logic of Item variant without Website Item (backport #29383)
2022-01-21 15:35:16 +05:30
mergify[bot]
0da75d745e fix: update status on visit cancel (#29389) (#29395)
* fix: update status on cancelling visit

* test: updated test for cancelling visits

(cherry picked from commit 11c9a5a79f)

Co-authored-by: Noah Jacob <noahjacobkurian@gmail.com>
2022-01-21 15:18:15 +05:30
marination
522031fd73 fix: (test) set user for price test
(cherry picked from commit 2185016f71)
2022-01-21 09:46:15 +00:00
marination
927efffcb7 fix: Sider and Linter
(cherry picked from commit 33b5e1d7e6)
2022-01-21 09:46:15 +00:00
marination
454ae7e0ed tests: Variant without web item price fetch and add to cart
- Also, optionally get image from template web item for cart if variant has no image

(cherry picked from commit c1dd3d7d9e)
2022-01-21 09:46:14 +00:00
marination
c81cf33dce fix: Cart Logic of Item variant without Website Item
(cherry picked from commit 7d130af12b)
2022-01-21 09:46:14 +00:00
Deepesh Garg
84803f8f21 Merge pull request #29370 from frappe/mergify/bp/version-13-pre-release/pr-29366
fix: Cleanup empty rows on bank statement import (backport #29366)
2022-01-20 12:14:51 +05:30
Deepesh Garg
6a53cfea00 fix: Cleanup empty rows on bank statement import
(cherry picked from commit 25398d017b)
2022-01-20 05:56:08 +00:00
Marica
56dd25355d Merge pull request #29357 from frappe/mergify/bp/version-13-pre-release/pr-29352
refactor: update_serial_no function for old Maintenance Visits (backport #28843) (backport #29352)
2022-01-19 16:40:44 +05:30
marination
fa7f5d745e fix: Patch (reload doc)
(cherry picked from commit 93d665b38a)
2022-01-19 09:19:06 +00:00
marination
dd81d622dd fix: Conflicts
(cherry picked from commit 3023757a60)
2022-01-19 09:19:06 +00:00
Noah Jacob
4086f4f8d1 fix: Serial No. filters for unscheduled visits
(cherry picked from commit dec7513773)
(cherry picked from commit add36363ab)
2022-01-19 09:19:05 +00:00
Noah Jacob
311b2dd1a9 refactor: moved purpose table mandatory check to server
(cherry picked from commit b98c61f2a7)

# Conflicts:
#	erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
(cherry picked from commit 93325c72f0)
2022-01-19 09:19:05 +00:00
Noah Jacob
0783b36ba0 fix(test): fixed test case not creating serials
(cherry picked from commit 90d32006f4)
(cherry picked from commit 0bb0a9d032)
2022-01-19 09:19:04 +00:00
Noah Jacob
f491cc92cd test: added test for serial no. filters
(cherry picked from commit 9109660a69)
(cherry picked from commit f541b5c780)
2022-01-19 09:19:04 +00:00
Noah Jacob
824eef9354 fix(patch): updates maintenance schedule field for old docs
(cherry picked from commit c872aa43f8)

# Conflicts:
#	erpnext/patches.txt
(cherry picked from commit dc0d974144)
2022-01-19 09:19:04 +00:00
Noah Jacob
216f45ce11 refactor: update_serial_no function and code cleanup
(cherry picked from commit 4f52b86d7e)

# Conflicts:
#	erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js
(cherry picked from commit 8775154fa0)
2022-01-19 09:19:03 +00:00
mergify[bot]
3064d95889 refactor: convert CTE validation to python (#29324) (#29332)
Some old servers running on mariadb <10.2 can't work with CTEs hence
reverting to basic python code

Co-authored-by: Ankush Menat <ankush@frappe.io>
(cherry picked from commit d7f16c4924)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2022-01-18 11:48:01 +05:30
rohitwaghchaure
ac2d1a9acc Merge pull request #29329 from rohitwaghchaure/merge-hotfix-to-pre-release-for-13-19
chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release'
2022-01-18 00:23:29 +05:30
Rohit Waghchaure
8d3f8619cf chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release' 2022-01-18 00:01:14 +05:30
Rohit Waghchaure
dfe2140f70 Merge branch 'version-13-pre-release' into version-13 2022-01-06 23:59:30 +05:30
Rohit Waghchaure
c4dab3eef3 bumped to version 13.18.0 2022-01-07 00:19:30 +05:50
rohitwaghchaure
e967aafc28 Merge pull request #29178 from rohitwaghchaure/change-log-for-v13-18
chore: change log for v13.18.0
2022-01-06 23:58:02 +05:30
Rohit Waghchaure
0e925c6ea2 chore: change log for v13.18.0 2022-01-06 23:56:24 +05:30
mergify[bot]
49c33a67e1 fix: incorrect serial no valuation report showing cancelled entries (#29172) (#29174)
(cherry picked from commit 24fc487dd8)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-01-06 17:07:05 +05:30
mergify[bot]
2f98aa121a fix: Earned Leave allocation from Leave Policy Assignment (backport #29163) (#29166)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-01-06 11:46:37 +05:30
Subin Tom
d3820f6ce3 feat: 'Invoice Number' field in Opening Invoice Creation Tool (#29147) 2022-01-05 11:21:15 +05:30
rohitwaghchaure
2f07d38caf Merge pull request #29150 from frappe/mergify/bp/version-13-pre-release/pr-29103
fix: incorrect posting time fetching incorrect qty (backport #29103)
2022-01-05 10:36:37 +05:30
rohitwaghchaure
48f98a68b8 fix: incorrect posting time fetching incorrect qty (#29103)
(cherry picked from commit f02e6b4631)
2022-01-05 04:23:01 +00:00
Deepesh Garg
f90b49a1b5 Merge pull request #29145 from frappe/mergify/bp/version-13-pre-release/pr-29142
fix: map Accounting Dimensions for Bank Entry against Payroll Entry (backport #29142)
2022-01-04 21:02:16 +05:30
Rucha Mahabal
1eccb83904 fix: map Accounting Dimensions for Bank Entry against Payroll Entry
(cherry picked from commit 5eeb94db31)
2022-01-04 14:58:45 +00:00
Subin Tom
0b07fbd5a2 fix: Modifying Opening invoice creation tool timestamp (#29139) 2022-01-04 19:15:59 +05:30
mergify[bot]
3898b19086 fix: future recurring period calculation (backport #29083) (#29119)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2022-01-04 12:00:42 +05:30
mergify[bot]
8a8648e6ec fix: filter query in bank reconciliation tool (#29100) 2022-01-03 11:41:38 +05:30
Deepesh Garg
19154074c1 Merge pull request #29095 from frappe/mergify/bp/version-13-pre-release/pr-29092
fix(India): Tax and Charges template not getting fetched based on tax category assigned (backport #29092)
2022-01-02 19:30:46 +05:30
Deepesh Garg
9a32bb6b51 fix: Test Case
(cherry picked from commit 342658ea70)
2022-01-02 13:37:23 +00:00
Deepesh Garg
cf4cac05ab fix(India): Tax and Charges template not getting fetched based on tax category assigned
(cherry picked from commit 7a5937a98c)
2022-01-02 13:37:23 +00:00
rohitwaghchaure
0703c1f996 Merge pull request #29073 from rohitwaghchaure/merge-hotfix-to-pre-release-for-13-18
chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release'
2021-12-29 23:29:28 +05:30
Rohit Waghchaure
b378a225d0 chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release' 2021-12-29 23:10:55 +05:30
Rohit Waghchaure
498d933e9c Merge branch 'version-13-pre-release' into version-13 2021-12-21 17:25:46 +05:30
Rohit Waghchaure
5c078198a3 bumped to version 13.17.0 2021-12-21 17:45:46 +05:50
rohitwaghchaure
2ef86fb875 Merge pull request #28989 from rohitwaghchaure/change-log-for-v13-17
chore: change log for v13.17.0
2021-12-21 17:24:13 +05:30
Rohit Waghchaure
30a1512cae chore: change log for v13.17.0 2021-12-21 17:18:56 +05:30
mergify[bot]
b2de0e7bbd fix: Reset value_after_depreciation on reversing journal entry during Asset return (#28981)
(cherry picked from commit 1ed30ee7c7)

Co-authored-by: Ganga Manoj <ganga.manoj98@gmail.com>
2021-12-21 15:08:49 +05:30
Deepesh Garg
b387fe54a6 Merge pull request #28962 from frappe/mergify/bp/version-13-pre-release/pr-28959
fix: Error on viewing consolidated financial statement (backport #28959)
2021-12-20 17:15:32 +05:30
Deepesh Garg
d60f51dd09 fix: Error on viewing consolidated financial statement
(cherry picked from commit 5d6199cf35)
2021-12-20 11:44:25 +00:00
mergify[bot]
f2464ce04a fix: incorrect rounding off near zero (#28948) (#28949)
(cherry picked from commit 0724a148e6)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2021-12-18 21:36:17 +05:30
mergify[bot]
5a6ea7a829 fix(ksa): enable ksa pos invoice print format (#28934) 2021-12-17 15:27:23 +05:30
mergify[bot]
3dd12143a2 fix: mapping to maintenance visit gets erased (#28917) (#28926)
* fix: mappings to maintenance visit gets erased

* test: mapping of schedule to visit

(cherry picked from commit 4cb5b627a1)

Co-authored-by: Noah Jacob <noahjacobkurian@gmail.com>
2021-12-16 23:33:43 +05:30
mergify[bot]
ac17bc3d31 chore: ledger invariant check report (#28921) (#28923)
(cherry picked from commit 6ba8f7644d)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2021-12-16 22:48:33 +05:30
mergify[bot]
6b248db0c9 fix: hsn-wise summary is incorrect if an invoice has repeated item code (#28892) 2021-12-15 11:45:59 +05:30
mergify[bot]
a712e5cee6 revert: "fix: expected value after useful life validation" (#28891) 2021-12-15 11:09:34 +05:30
Deepesh Garg
aee4f87b07 Merge pull request #28868 from frappe/mergify/bp/version-13-pre-release/pr-28832
fix: KSA QR field rename patch (backport #28832)
2021-12-14 16:02:06 +05:30
mergify[bot]
0d8ad4d64b fix: reorder patch to fix custom field options (backport #28866)
* fix: reorder patch to fix custom field options (#28866)

(cherry picked from commit 7fd06108bf)

# Conflicts:
#	erpnext/patches.txt

* fix: resolve conflict

Co-authored-by: Ankush Menat <ankush@frappe.io>
(cherry picked from commit 187a69e390)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2021-12-14 14:05:21 +05:30
Deepesh Garg
198b427349 fix: KSA QR field rename patch
(cherry picked from commit 14d03a9446)
2021-12-14 07:18:54 +00:00
mergify[bot]
0e68037082 fix: migration hashes (#28850) (#28860)
Co-authored-by: Ankush Menat <ankush@frappe.io>
(cherry picked from commit 46a6c8dbfe)

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2021-12-14 11:13:58 +05:30
rohitwaghchaure
e08c9cc250 Merge pull request #28855 from rohitwaghchaure/merge-hotfix-to-pre-release-for-13-17
chore: merge branch 'version-13-hotfix' into 'version-13-pre-release'
2021-12-14 00:47:47 +05:30
Rohit Waghchaure
a3189226a5 chore: merge branch 'version-13-hotfix' into 'version-13-pre-release' 2021-12-13 23:01:11 +05:30
Deepesh Garg
57b70ace9b Merge pull request #28791 from deepeshgarg007/version-13-tax-hotfix
fix: Error on Invoice generation
2021-12-08 14:15:52 +05:30
Deepesh Garg
4fe5d8b1ae fix: Error on Invoice generation 2021-12-08 14:14:04 +05:30
Deepesh Garg
f22bbaa9ba Merge pull request #28788 from frappe/mergify/bp/version-13-pre-release/pr-28786
fix: Error on Invoice generation (backport #28786)
2021-12-08 14:12:00 +05:30
Deepesh Garg
f478d2d36a fix: Error on Invoice generation
(cherry picked from commit 82255293c4)
2021-12-08 08:40:05 +00:00
Rohit Waghchaure
fa304f3300 Merge branch 'version-13-pre-release' into version-13 2021-12-08 13:27:11 +05:30
Rohit Waghchaure
603b1888fc bumped to version 13.16.1 2021-12-08 13:47:11 +05:50
Deepesh Garg
20f9015620 Merge pull request #28784 from frappe/mergify/bp/version-13-pre-release/pr-28777
fix: Error on creating invoice (backport #28777)
2021-12-08 12:13:08 +05:30
Deepesh Garg
153c9221a9 fix: Error on creating invoice
(cherry picked from commit e11515a356)
2021-12-08 06:42:36 +00:00
Rohit Waghchaure
5c1a2aee3a Merge branch 'version-13-pre-release' into version-13 2021-12-06 00:24:07 +05:30
Rohit Waghchaure
0e8981028e bumped to version 13.16.0 2021-12-06 00:44:06 +05:50
rohitwaghchaure
55c653a7f8 Merge pull request #28737 from rohitwaghchaure/change-log-for-v13-16
chore: change log for v13.16.0
2021-12-06 00:20:44 +05:30
Rohit Waghchaure
6398e588ec chore: change log for v13.16.0 2021-12-05 23:56:31 +05:30
mergify[bot]
b6ef93d574 fix(patch): create only component type field instead of running the whole setup (#28734) (#28736)
(cherry picked from commit 0ef42d1000)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2021-12-05 13:05:34 +05:30
Noah Jacob
ddcbea3d0a test: added tests for manufacture stock entry when material_consumption is enabled
(cherry picked from commit 35346de162)
2021-12-04 18:51:16 +05:30
Noah Jacob
27c8c361d6 fix: incorrect outgoing rates when material_consumption enabled
(cherry picked from commit 7f3e6d149a)
2021-12-04 18:51:16 +05:30
mergify[bot]
e1c209973c fix(ksa): qrcode for invoices with special chars (#28717) 2021-12-03 16:38:26 +05:30
Deepesh Garg
4c1d1555d2 Merge pull request #28711 from frappe/mergify/bp/version-13-pre-release/pr-28708
fix: Invoice amount in KSA E Invoice QR Code (backport #28708)
2021-12-03 15:47:20 +05:30
Deepesh Garg
64feb2862b Merge pull request #28697 from frappe/mergify/bp/version-13-pre-release/pr-28654
fix: Taxes and Charges template not getting copied from Purchase Order/Receipt to Invoice (backport #28654)
2021-12-03 15:46:53 +05:30
Deepesh Garg
6fd7460abf fix: Invocie amount in KSA E Invoice QR Code
(cherry picked from commit f2ffddf059)
2021-12-03 09:54:46 +00:00
Deepesh Garg
e4c9581d6e Merge pull request #28642 from frappe/mergify/bp/version-13-pre-release/pr-28302
fix: Shipping Rule picking up old net_rate (backport #28302)
2021-12-03 15:20:28 +05:30
mergify[bot]
97dd2dbc56 fix: qrcode image name for invoices with special chars (#28702) 2021-12-03 11:53:02 +05:30
Deepesh Garg
6bb2051c11 fix: India utils code cleanup
(cherry picked from commit 56c626adbf)
2021-12-03 04:11:55 +00:00
Deepesh Garg
a5163b0879 fix: Taxes and Charges template not getting copied from Purchase Order/Receipt to Invoice
(cherry picked from commit 6a75e8d283)
2021-12-03 04:11:54 +00:00
Deepesh Garg
519394e1fc Merge pull request #28682 from frappe/mergify/bp/version-13-pre-release/pr-28533
fix: misc minor Product Bundle fixes (backport #28533)
2021-12-02 19:14:32 +05:30
mergify[bot]
b47322af5a fix: remove change_abbr method (#28692) 2021-12-02 15:31:30 +05:30
mergify[bot]
abfaa4c07e fix: dont requeue repost immediately and clear progress (#28684) (#28690)
(cherry picked from commit a37c99a23d)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2021-12-02 14:58:24 +05:30
GangaManoj
1ec0cd4e5b fix: Add item to packing list
(cherry picked from commit a473e1dbe9)
2021-12-02 03:52:56 +00:00
GangaManoj
1b8a947a18 fix: Remove unnecessary comma
(cherry picked from commit c9743185c6)
2021-12-02 03:52:55 +00:00
GangaManoj
ad96511fba fix: Test Product Bundle price calculation when there are multiple Product Bundles
(cherry picked from commit adfd519139)
2021-12-02 03:52:54 +00:00
GangaManoj
2353fb64d8 fix: Test that indices are reset for Packed/Bundle Items when Product Bundles are removed from the Items table
(cherry picked from commit 325923afc7)
2021-12-02 03:52:54 +00:00
GangaManoj
eb851dd36a fix: Reset indices in the Packed/Bundle Items table on deleting Product Bundles
(cherry picked from commit 8370042f82)
2021-12-02 03:52:53 +00:00
GangaManoj
c9d3fa0649 fix: Fix Product Bundle price calculation when there are multiple Product Bundles
(cherry picked from commit 0803f87660)
2021-12-02 03:52:52 +00:00
GangaManoj
4a38e61345 fix: Add bundle items to PO only if the Product Bundle was selected from the SO
(cherry picked from commit 406278b5c1)
2021-12-02 03:52:51 +00:00
mergify[bot]
dd2ce664ea test: dynamic fiscal year creation in tests (#28667) (#28671)
(cherry picked from commit fdffa037b5)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2021-12-01 17:53:44 +05:30
mergify[bot]
54056b8150 chore: add timeout to GHA workflows (#26714) (#28662)
(cherry picked from commit 0bb60b37df)

Co-authored-by: Ankush <ankush@iwebnotes.com>
2021-12-01 11:24:20 +05:30
mergify[bot]
62c5fd2c4c fix: Unable to search project by project name in Sales Invoice (bp #28648)
(cherry picked from commit 08b7c856b2)

Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
2021-11-30 18:43:22 +05:30
Subin Tom
10c92c45d8 fix: merge conflicts 2021-11-30 16:11:29 +05:30
mergify[bot]
6c5cefb3ac fix: allow creating Shift Assignment for same day (#28613) (#28630)
(cherry picked from commit 4458b24813)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2021-11-30 14:42:36 +05:30
Subin Tom
b1c1b0ce49 fix: sider issues
(cherry picked from commit e7b4204c35)

# Conflicts:
#	erpnext/public/js/controllers/taxes_and_totals.js
2021-11-30 09:08:52 +00:00
Subin Tom
fcfd6d6f04 fix: fixed tests, separated a method for shipping charges
(cherry picked from commit a8e2c02e14)
2021-11-30 09:08:51 +00:00
Subin Tom
8d0493113a fix: check if shipping rule value exists
(cherry picked from commit af1fce0419)
2021-11-30 09:08:50 +00:00
Subin Tom
edf446e632 fix: calling shipping rule method during net_total calculation in taxes_adn_totals.py
(cherry picked from commit 18ae03d967)
2021-11-30 09:08:50 +00:00
Subin Tom
bf212da25a fix: Shipping Rule picking up old net_rate
(cherry picked from commit c78b8b7897)

# Conflicts:
#	erpnext/public/js/controllers/taxes_and_totals.js
#	erpnext/public/js/controllers/transaction.js
2021-11-30 09:08:50 +00:00
mergify[bot]
3d76afbc7b fix: Employee Transfer and Project Profitability test cases (#28633) (#28640)
* fix: Employee Transfer testcases

* fix: Project Profitability test case

(cherry picked from commit d0f4f03b66)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2021-11-30 14:26:57 +05:30
rohitwaghchaure
bf62c7634a Merge pull request #28622 from rohitwaghchaure/merge-hotfix-to-pre-release-for-13-16
chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release'
2021-11-29 21:08:49 +05:30
Rohit Waghchaure
ced223188f chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release' 2021-11-29 19:41:48 +05:30
Rohit Waghchaure
db7fbfb5a7 Merge branch 'version-13-pre-release' into version-13 2021-11-29 19:18:12 +05:30
Rohit Waghchaure
f1fb0fc5cb bumped to version 13.15.2 2021-11-29 19:38:12 +05:50
Deepesh Garg
6feb0df8e8 Merge pull request #28621 from frappe/mergify/bp/version-13-pre-release/pr-28464
fix:Change QR Code Triggerr event (backport #28464)
2021-11-29 19:15:13 +05:30
Deepesh Garg
ca375b0e8d fix: Move trigger from on trash to on cancel
(cherry picked from commit 4382040fb6)
2021-11-29 13:18:15 +00:00
Mohammed Redah
26932885bb fix:Change QR Code Triggerr event
This fixes the bug if the user changes the date after insertion it will show the wrong values

(cherry picked from commit 9d319c2205)
2021-11-29 13:18:14 +00:00
Deepesh Garg
69c0e4dea1 Merge pull request #28612 from frappe/mergify/bp/version-13-pre-release/pr-28605
refactor(KSA VAT): QR Code as per ZATKA specification (backport #28605)
2021-11-29 17:38:26 +05:30
Deepesh Garg
9348fcf6c3 fix: KSA print format for invoices not having item codes
(cherry picked from commit af6fc29770)
2021-11-29 11:17:04 +00:00
Deepesh Garg
3289ba92fa fix: Translations
(cherry picked from commit f3f7ed6f0d)
2021-11-29 11:17:03 +00:00
Ahmad
f2f00ad402 fix: KSA VAT QR Code arabic conversion
(cherry picked from commit 31b9b84fdf)
2021-11-29 11:17:03 +00:00
Ahmad
641043e893 refactor: comments removed
(cherry picked from commit de784d8bfe)
2021-11-29 11:17:02 +00:00
Ahmad
fda69bc0cf refactor(KSA VAT): QR Code as per ZATKA specification
(cherry picked from commit d1746caa02)
2021-11-29 11:17:01 +00:00
Rohit Waghchaure
a0579ece92 Merge branch 'version-13-pre-release' into version-13 2021-11-26 12:38:51 +05:30
Rohit Waghchaure
652b7393dc bumped to version 13.15.1 2021-11-26 12:58:50 +05:50
mergify[bot]
46df181d00 fix: do not get password if client_secret is not set (#28554) 2021-11-25 13:45:44 +05:30
Rohit Waghchaure
c92dfa988e Merge branch 'version-13-pre-release' into version-13 2021-11-22 22:50:39 +05:30
Rohit Waghchaure
d03bc7d8dc bumped to version 13.15.0 2021-11-22 23:10:38 +05:50
rohitwaghchaure
742249b6c9 Merge pull request #28508 from rohitwaghchaure/change-log-for-v13-15
chore: change log for v13.15.0
2021-11-22 22:48:43 +05:30
Rohit Waghchaure
24c21c7f49 chore: change log for v13.15.0 2021-11-22 22:45:58 +05:30
rohitwaghchaure
c48f60e6e2 Merge pull request #28506 from frappe/revert-28499-mergify/bp/version-13-pre-release/pr-28491
revert: "feat: Accounts, Selling & Assets Onboarding cleanup #28499"
2021-11-22 22:31:54 +05:30
Saqib
8824712feb Revert "feat: Accounts, Selling & Assets Onboarding cleanup (#28499)"
This reverts commit 145bcbaa6d.
2021-11-22 20:26:44 +05:30
Deepesh Garg
3b2b9ce70c Merge pull request #28503 from frappe/mergify/bp/version-13-pre-release/pr-28469
fix: FY query returning None for new company (backport #28469)
2021-11-22 20:20:17 +05:30
Subin Tom
953f46e289 fix: FY query returning None for new company
(cherry picked from commit 350ed1a6c2)
2021-11-22 13:54:11 +00:00
mergify[bot]
761bd8c97b fix: KSA Invoice print format for multicurrency invoices (#28495) 2021-11-22 18:25:37 +05:30
mergify[bot]
145bcbaa6d feat: Accounts, Selling & Assets Onboarding cleanup (#28499) 2021-11-22 18:24:51 +05:30
mergify[bot]
1b4659f028 fix: Closed status error in Work Order Summary (backport #28460)
* fix: Closed status error in Work Order Summary (#28460)

* fix: Closed status error in Work Order Summary

* chore: use get_meta to get status options

* refactor: simplify code

Co-authored-by: Ankush Menat <ankush@frappe.io>
(cherry picked from commit aa689874e3)

# Conflicts:
#	erpnext/manufacturing/report/work_order_summary/work_order_summary.py

* fix: resolve conflict

Co-authored-by: Sagar Sharma <63660334+s-aga-r@users.noreply.github.com>
Co-authored-by: Ankush Menat <ankush@frappe.io>
2021-11-22 13:23:22 +05:30
mergify[bot]
e40d63dc13 fix(e-invoicing): totals validation of e-invoices (#28476) 2021-11-22 11:23:33 +05:30
Deepesh Garg
2a165d6250 Merge pull request #28474 from frappe/mergify/bp/version-13-pre-release/pr-28471
fix: bug with qrcode generation due to default print format name (backport #28471)
2021-11-22 11:16:53 +05:30
Saqib Ansari
2c6d973dd6 fix: bug with qrcode generation due to default print format name
(cherry picked from commit ac27391a1f)
2021-11-22 05:08:15 +00:00
Deepesh Garg
007057e5f2 Merge pull request #28462 from frappe/mergify/bp/version-13-pre-release/pr-28459
fix(India): GST category not getting auto updated (backport #28459)
2021-11-19 13:06:29 +05:30
Deepesh Garg
08207242c1 fix: Add test for gst category check
(cherry picked from commit cdbc991e3f)
2021-11-19 06:52:22 +00:00
Deepesh Garg
24c113bf68 fix(India): GST category not getting auto updated
(cherry picked from commit f8a26a9fac)
2021-11-19 06:52:22 +00:00
Ankush Menat
bceeb0f751 test: basic test for serialize reco cancel
(cherry picked from commit ed99aca36f)
2021-11-18 14:35:13 +05:30
Ankush Menat
cf9397eea0 fix: can not cancel stock reconciliation with sr no
(cherry picked from commit 8c85012a70)
2021-11-18 14:35:13 +05:30
mergify[bot]
6fdd5e990a chore: remove manual collapsing of section (#28435) (#28451)
(cherry picked from commit 8102e4a210)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2021-11-18 14:14:44 +05:30
mergify[bot]
b4db3362e0 fix: added job_card_item link in material request (#28222) (#28434)
* fix: added job_card_item links in material request

* fix: add no copy to row references

(cherry picked from commit cb064b06ad)

Co-authored-by: Noah Jacob <noahjacobkurian@gmail.com>
2021-11-17 19:37:20 +05:30
rohitwaghchaure
16b90688bb Merge pull request #28430 from frappe/mergify/bp/version-13-pre-release/pr-28417
fix: currency wise pricing rule not working (backport #28417)
2021-11-17 14:38:51 +05:30
Rohit Waghchaure
c118fa1ada fix: currency wise pricing rule not working
(cherry picked from commit 43aeb541c2)
2021-11-17 08:33:33 +00:00
rohitwaghchaure
801e6df37a Merge pull request #28426 from frappe/mergify/bp/version-13-pre-release/pr-28425
fix: performance issue while submitting the Journal Entry (backport #28425)
2021-11-17 13:34:30 +05:30
Rohit Waghchaure
70a130e78d fix: performance to submit the JV
(cherry picked from commit 7472760ea3)
2021-11-17 07:25:29 +00:00
rohitwaghchaure
af251e439a Merge pull request #28419 from rohitwaghchaure/merge-hotfix-to-pre-release-for-13-15
chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release'
2021-11-16 22:37:01 +05:30
Rohit Waghchaure
d556645455 Merge branch 'version-13-hotfix' into 'version-13-pre-release' 2021-11-16 21:25:53 +05:30
Rohit Waghchaure
575d41c024 Merge branch 'version-13-pre-release' into version-13 2021-11-12 23:34:56 +05:30
Rohit Waghchaure
7fc2f24470 bumped to version 13.14.1 2021-11-12 23:54:56 +05:50
Deepesh Garg
2747f263c7 Merge pull request #28366 from frappe/mergify/bp/version-13-pre-release/pr-28363
fix: Default party account getting overriden in invoices (backport #28363)
2021-11-12 13:38:28 +05:30
Deepesh Garg
b0a58f4111 fix: Default party account getting overriden in invoices
(cherry picked from commit 88648570d7)
2021-11-12 07:35:03 +00:00
Saqib
6661cc7703 fix(pos): get mode of payments query (#28323) 2021-11-10 13:38:21 +05:30
Rohit Waghchaure
ab904c22cc Merge branch 'version-13-pre-release' into version-13 2021-11-09 20:16:01 +05:30
Rohit Waghchaure
6b77bac7da bumped to version 13.14.0 2021-11-09 20:36:00 +05:50
rohitwaghchaure
b9da89a90b Merge pull request #28305 from rohitwaghchaure/change-log-for-v13-14-0
chore: Added change log for version 13.14.0
2021-11-09 20:13:44 +05:30
Rohit Waghchaure
f3cfb93a5f chore: Added change log for version 13.14.0 2021-11-09 20:09:09 +05:30
mergify[bot]
fa2f1348ec fix(India setup): setup company independent fixtures for patch (#28304)
(cherry picked from commit 88b5bda34b)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2021-11-09 19:50:14 +05:30
rohitwaghchaure
346c76cf38 Merge pull request #28290 from frappe/mergify/bp/version-13-pre-release/pr-28150
feat: provision to close the Work Order (backport #28150)
2021-11-09 16:38:10 +05:30
rohitwaghchaure
81e7ebc112 Merge pull request #28293 from frappe/mergify/bp/version-13-pre-release/pr-28219
feat: provision to have limited parameters for in-process quality inspection (backport #28219)
2021-11-09 16:37:37 +05:30
Diksha Jadhav
bb9713a7b2 refactor: use fetch from property for qi template on job card
(cherry picked from commit 467324c87f)
2021-11-09 10:23:00 +00:00
Diksha Jadhav
ac54e80856 feat: provision to have limited parameters for in-process quality inspection
(cherry picked from commit 6c45f8b2c4)
2021-11-09 10:22:59 +00:00
Diksha Jadhav
3c78ac21d9 feat(manufacturing): add link field for quality inspection template on operation
(cherry picked from commit 281a9cc749)
2021-11-09 10:22:58 +00:00
Anupam
515b8dd8ca fix: get_planned_qty chnages
(cherry picked from commit 663a7afe4d)
2021-11-09 10:16:26 +00:00
Anupam
dac0d87e16 fix: test cases
(cherry picked from commit 27709a1c71)
2021-11-09 10:16:25 +00:00
Anupam
365572f5ea fix: testcases:
(cherry picked from commit 7044ae5e39)
2021-11-09 10:16:25 +00:00
Anupam
029e66709d fix: linter isuue
(cherry picked from commit cc15cf6ae2)
2021-11-09 10:16:24 +00:00
Anupam
8851b7adcf fix: sider isuue
(cherry picked from commit ba47bd02b6)
2021-11-09 10:16:24 +00:00
Anupam
116210dcea fix: validate job card
(cherry picked from commit 9b4c7e4796)
2021-11-09 10:16:23 +00:00
Anupam
dce943b41f fix: test cases
(cherry picked from commit 9c0906f1b5)
2021-11-09 10:16:23 +00:00
Anupam
cb209d9b57 fix: linter issues
(cherry picked from commit e36da4d137)
2021-11-09 10:16:23 +00:00
Anupam
7c7493c91d fix: linter issues
(cherry picked from commit 59e4fd980c)
2021-11-09 10:16:22 +00:00
Anupam
f3da88e301 fix: added testcase
(cherry picked from commit 530a0f481e)
2021-11-09 10:16:22 +00:00
Anupam
8ddeb83191 fix: sider issue
(cherry picked from commit 264b0df9ff)
2021-11-09 10:16:21 +00:00
Anupam
47f8f3d9dd fix: sider issue
(cherry picked from commit 55e97dce8a)
2021-11-09 10:16:20 +00:00
Anupam
44c53deb90 fix: sider issue
(cherry picked from commit e290fe0721)
2021-11-09 10:16:20 +00:00
Anupam
d43aecbe13 feat: added confirm dialog on closing of workorder
(cherry picked from commit 5d4c5652af)
2021-11-09 10:16:19 +00:00
Anupam
e8ef54f4d7 feat: provision to close the Work Order
(cherry picked from commit 23af036894)
2021-11-09 10:16:18 +00:00
mergify[bot]
eacf26d618 fix: show full item name in search widget (#28283) (#28285)
(cherry picked from commit 34f5283c17)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2021-11-09 12:14:54 +05:30
mergify[bot]
40b5bb1d15 fix: do not generate multiple invoices (#28216) 2021-11-08 20:07:57 +05:30
Deepesh Garg
5d46f765a6 Merge pull request #28276 from frappe/mergify/bp/version-13-pre-release/pr-28262
fix: Taxjar customer_address fix, currency fix (backport #28262)
2021-11-08 19:04:06 +05:30
Deepesh Garg
2654098ce2 Merge pull request #28274 from frappe/mergify/bp/version-13-pre-release/pr-28272
fix: KSA VAT setup issues (backport #28272)
2021-11-08 18:52:20 +05:30
Subin Tom
a403200516 fix: company condition fix, added company field
(cherry picked from commit 7ad2717acc)
2021-11-08 13:12:00 +00:00
Subin Tom
1dc7cb8ad1 fix: fixed company field, updated patch
(cherry picked from commit 7f2d304f32)
2021-11-08 13:12:00 +00:00
Subin Tom
abe6ef4cd4 fix: Added company field, filtered account heads
(cherry picked from commit 902c03cd37)
2021-11-08 13:12:00 +00:00
Subin Tom
3d1b833f35 fix: Fixed customer address variable, sales invoice item field currency issue
(cherry picked from commit 904010ab64)
2021-11-08 13:11:59 +00:00
Deepesh Garg
2dc12505b2 fix: Resolve conflicts 2021-11-08 18:16:46 +05:30
Deepesh Garg
f5251b5a86 fix: Add patch to make custom fields
(cherry picked from commit 508832e90a)

# Conflicts:
#	erpnext/patches.txt
2021-11-08 12:39:55 +00:00
Deepesh Garg
aa6caa00bf fix: KSA VAT setup issues
(cherry picked from commit c81d4734c4)
2021-11-08 12:39:54 +00:00
Deepesh Garg
35bd31e939 Merge pull request #28270 from frappe/mergify/bp/version-13-pre-release/pr-28166
feat(Print Format): KSA VAT Invoice (backport #28166)
2021-11-08 17:15:49 +05:30
ahmadpak
0ba59049f9 update(Print Format): Sales Invoice
- KSA VAT Invoice
Customer Identification Number Added

(cherry picked from commit 8df50cf1e0)
2021-11-08 09:58:11 +00:00
ahmadpak
9f6b0dbfdf new (Print Format): KSA VAT Invoice
(cherry picked from commit 2a5beec885)
2021-11-08 09:58:11 +00:00
mergify[bot]
79be341ea4 feat: replace newline in remarks (DATEV report) (#28152) (#28248)
(cherry picked from commit 114028e473)

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2021-11-05 11:15:18 +05:30
Suraj Shetty
b3dc199118 Merge pull request #28191 from frappe/build-on-release
ci: migrate docker to github actions
2021-11-03 18:10:20 +05:30
Saqib
9a3c6694b9 Merge pull request #28221 from frappe/mergify/bp/version-13-pre-release/pr-27946
fix(Payment Entry): splitting outstanding rows as per payment terms (backport #27946)
2021-11-03 15:41:29 +05:30
Anuja Pawar
2c9266807a fix(Payment Entry): splitting outstanding rows as per payment terms (#27946)
(cherry picked from commit d72709dd81)
2021-11-03 09:18:40 +00:00
Revant Nandgaonkar
afb236a06b ci: change GITHUB_PAT to CI_PAT 2021-11-03 13:56:50 +05:30
mergify[bot]
94905df774 fix: ignore unsupported methods while resyncing (#28210) (#28214)
(cherry picked from commit 72a050fb0b)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2021-11-03 09:47:00 +05:30
Marica
fa5cee479f Merge pull request #28204 from frappe/mergify/bp/version-13-pre-release/pr-28202
fix: Serial Nos not set in the row after scanning in popup (backport #28202)
2021-11-02 20:41:33 +05:30
Deepesh Garg
885f1e0fd8 Merge pull request #28207 from frappe/mergify/bp/version-13-pre-release/pr-28205
fix: Error on LDC creation (backport #28205)
2021-11-02 20:29:43 +05:30
Deepesh Garg
e358d34c4f fix: Error on LDC creation
(cherry picked from commit 66348e1a03)
2021-11-02 14:59:10 +00:00
marination
093d9f4a70 fix: Serial Nos not set in the row after scanning in popup
- Avoid whitspaces while calculating length of serial nos

(cherry picked from commit 734b57deec)
2021-11-02 14:48:40 +00:00
mergify[bot]
50fbc167f3 fix(Payment Reconciliation): clear child tables on company/party change (#28194) 2021-11-02 17:12:11 +05:30
Marica
0731454f50 Merge pull request #28196 from frappe/mergify/bp/version-13-pre-release/pr-28195
fix: (ux) Remove warehouse filter on Batch field for Material Receipt (backport #28195)
2021-11-02 16:28:02 +05:30
marination
b81dfdcd26 chore: Add comment above fix for future reference
(cherry picked from commit 48886ee705)
2021-11-02 10:40:02 +00:00
marination
68809159ba fix: Remove warehouse filter on Batch field for Material Receipt
(cherry picked from commit 048210a8f6)
2021-11-02 10:40:00 +00:00
Revant Nandgaonkar
1736029733 chore: migrate docker to github actions 2021-11-02 12:03:22 +05:30
Deepesh Garg
982960fc0a Merge pull request #28177 from frappe/mergify/bp/version-13-pre-release/pr-26735
fix: COGS account in purchase receipt (backport #26735)
2021-11-01 16:51:11 +05:30
Deepesh Garg
bb7d661a04 fix: Resolve conflicts 2021-11-01 16:23:39 +05:30
Ankush Menat
c6557a136c fix: use warehouse to find company
(cherry picked from commit f7ffe04a4b)
2021-11-01 15:31:11 +05:30
Ankush Menat
271dad941b fix: pass company while fetching valuation rate
If company is not supplied and valuation rate is 0, then default
company is used for checking if perpetual inventory is enabled or not.
This makes little sense as different companies can have different
setting for perpetual inventory.

(cherry picked from commit a0727b2e82)
2021-11-01 15:31:11 +05:30
Rohit Waghchaure
e9d1cbb339 fix: COGS account in purchase receipt
(cherry picked from commit 2a14f255cf)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
2021-11-01 07:26:09 +00:00
mergify[bot]
18dddfdea9 fix: remove duplicate field from selling_settings (#28174)
fix https://github.com/frappe/erpnext/issues/28155

(cherry picked from commit 1cc4eddacb)

Co-authored-by: ahmadRagheb <ahmedragheb75@gmail.com>
2021-11-01 11:11:40 +05:30
Deepesh Garg
978d1cde2d Merge pull request #28165 from frappe/mergify/bp/version-13-pre-release/pr-28162
fix: Error for missing PAN no field (backport #28162)
2021-10-31 18:50:10 +05:30
rohitwaghchaure
feaae330d5 Merge pull request #28170 from frappe/mergify/bp/version-13-pre-release/pr-28168
fix: patch update_category_in_ltds_certificate (backport #28168)
2021-10-31 10:28:09 +05:30
Rohit Waghchaure
060127663a fix: patch update_category_in_ltds_certificate
(cherry picked from commit cae29b71d8)
2021-10-31 04:27:51 +00:00
rohitwaghchaure
88bf748275 Merge pull request #28159 from rohitwaghchaure/merge-hotfix-to-pre-release-for-13-14
chore: merge branch 'version-13-hotfix' into 'version-13-pre-release'
2021-10-30 22:33:26 +05:30
Deepesh Garg
aa464cc615 fix: Error for missing PAN no field
(cherry picked from commit 541c892f97)
2021-10-30 14:00:07 +00:00
Rohit Waghchaure
8581b5cdea chore: merge branch 'version-13-hotfix' into 'version-13-pre-release' 2021-10-30 15:15:19 +05:30
Rohit Waghchaure
b8d75ff241 Merge branch 'version-13-pre-release' into version-13 2021-10-20 21:30:20 +05:30
Rohit Waghchaure
a3b7682935 bumped to version 13.13.0 2021-10-20 21:50:20 +05:50
rohitwaghchaure
200f6da8b2 Merge pull request #28040 from rohitwaghchaure/change-log-for-v13-13
chore: change log for v13.13.0
2021-10-20 21:23:39 +05:30
Rohit Waghchaure
7c9018f401 chore: change log for v13.13.0 2021-10-20 21:15:13 +05:30
Sagar Vora
ffadd671b7 fix: add mistakenly removed patches 2021-10-20 19:26:15 +05:30
mergify[bot]
47befa697d fix: incorrect status being set in Invoices (backport #28019) (#28030)
* fix: incorrect status being set in Invoices (#28019)

Co-authored-by: Pruthvi Patel <pruthvipatel145@gmail.com>
(cherry picked from commit 8d9d0987fe)

* fix: merge conflict

Co-authored-by: Sagar Vora <sagar@resilient.tech>
2021-10-20 19:24:19 +05:30
Jannat Patel
676c5280cc Merge pull request #28016 from frappe/mergify/bp/version-13-pre-release/pr-27728 2021-10-20 15:53:48 +05:30
Marica
e0decb0ae2 Merge pull request #28021 from frappe/mergify/bp/version-13-pre-release/pr-28005
fix: Fetch thumbnail from Item master instead of regenerating (backport #28005)
2021-10-20 15:14:30 +05:30
marination
b906cc20ae fix: Move thumbnail updation to different patch
- Thumbnail updation handled via different patch
- create_website_items will only have one purpose
- added progress bar to `create_website_items`
- code cleanup

(cherry picked from commit 348a961b53)
2021-10-20 09:12:33 +00:00
marination
c98421c69a fix: Check if thumbnail column exists in case of table trimming
(cherry picked from commit ac8014e24c)
2021-10-20 09:12:32 +00:00
marination
d7afb9ef65 fix: Get db values as dict when checking for thumbnail in existing web item
(cherry picked from commit 46a5a83789)
2021-10-20 09:12:32 +00:00
marination
a915b9cf72 fix: re-run patch
- Patch will just fetch thumbnails if website items are created, else it will create new website items

(cherry picked from commit 11c498d9e5)
2021-10-20 09:12:32 +00:00
marination
a022e01d3f fix: Fetch thumbnail from Item master instead of regenerating
(cherry picked from commit 94177c0764)
2021-10-20 09:12:31 +00:00
Goh Yan Chang
867cfa04b2 Update employee_leave_balance.py
fix: Employee Leave Balance report showing wrong figures
(cherry picked from commit 632f7848a3)
2021-10-20 06:58:12 +00:00
Jannat Patel
6e63dc1360 Merge pull request #28013 from frappe/mergify/bp/version-13-pre-release/pr-27904 2021-10-20 12:24:37 +05:30
Jannat Patel
698214bd59 fix: removed unused lines 2021-10-20 11:36:00 +05:30
Jannat Patel
873d166a4e fix: conflicts 2021-10-20 11:32:35 +05:30
Jannat Patel
9166d58717 fix: map missing fields in opportunity (#27904)
(cherry picked from commit d81f811349)

# Conflicts:
#	erpnext/crm/doctype/opportunity/opportunity.py
2021-10-20 05:56:59 +00:00
Deepesh Garg
7895d2a048 Merge pull request #28004 from frappe/mergify/bp/version-13-pre-release/pr-27867
fix: Totals row incorrect value in GL Entry (backport #27867)
2021-10-19 17:50:20 +05:30
Deepesh Garg
5a06ee9230 fix: Totals row incorrect value in GL Entry (#27867)
(cherry picked from commit ebe68c1a7a)
2021-10-19 09:39:02 +00:00
Noah Jacob
b6609d1649 Merge pull request #27998 from frappe/mergify/bp/version-13-pre-release/pr-27990
fix: changes in schedules gets overwritten on save (backport #27990)
2021-10-19 14:20:44 +05:30
Noah Jacob
9431bb9466 fix: changes in schedules gets overwritten on save
(cherry picked from commit af1b9e100e)
2021-10-19 08:07:42 +00:00
mergify[bot]
fdd9cc76be fix: flaky Org Chart Test (#27971) (#27989)
(cherry picked from commit 8eacaddde7)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2021-10-18 11:47:22 +05:30
Deepesh Garg
f8348ab681 Merge pull request #27983 from frappe/mergify/bp/version-13-pre-release/pr-27967
fix: Account number and name incorrectly imported using COA importer (backport #27967)
2021-10-18 11:04:39 +05:30
Deepesh Garg
eecfb25c90 Merge pull request #27981 from frappe/mergify/bp/version-13-pre-release/pr-27934
fix: TDS round off not working from second transaction (backport #27934)
2021-10-18 11:03:00 +05:30
Deepesh Garg
41a0e12954 Merge pull request #27979 from frappe/mergify/bp/version-13-pre-release/pr-27970
fix (India): Interstate internal transfer invoices not visible in GSTR-1 (backport #27970)
2021-10-18 11:01:06 +05:30
Ankush Menat
f0383289d8 Merge pull request #27986 from frappe/mergify/bp/version-13-pre-release/pr-27962
fix: Retain space inside Serial no string while cleaning serial nos (backport #27962)
2021-10-18 10:34:31 +05:30
Jannat Patel
46209023ce Merge pull request #27985 from frappe/mergify/bp/version-13-pre-release/pr-27850 2021-10-18 10:32:48 +05:30
marination
e69bd39cdd test: Include serial no with spaces in it in sanitation test
(cherry picked from commit a9341672cf)
2021-10-18 04:56:11 +00:00
Marica
0fcb3cd918 fix: Use strip instead of lstrip and rstrip
Co-authored-by: Ankush Menat <ankushmenat@gmail.com>
(cherry picked from commit 8cf188d9c0)
2021-10-18 04:56:10 +00:00
marination
44ab131792 fix: Retain space inside Serial no string while cleaning serial nos
(cherry picked from commit 41035b0330)
2021-10-18 04:56:10 +00:00
pateljannat
b648d77316 fix: exclude inactive employees from auto attendance
(cherry picked from commit 921b4be348)
2021-10-18 04:35:18 +00:00
Deepesh Garg
0012f0b2da fix: Account number and name incorrectly import using COA importer
(cherry picked from commit 17a8649500)
2021-10-18 03:47:22 +00:00
Deepesh Garg
1796f09c0f fix: TDS round off not working from second transaction
(cherry picked from commit b7a08535b5)
2021-10-18 03:24:46 +00:00
Deepesh Garg
64b58b148f fix: TDS round off not working from second transaction
(cherry picked from commit ca0067212d)
2021-10-18 03:24:46 +00:00
Deepesh Garg
4415bf9968 fix: Interstate internal transfer invoices not visible in GSTR-1
(cherry picked from commit d9d42b13ab)
2021-10-18 03:23:55 +00:00
rohitwaghchaure
1b7d94d70d Merge pull request #27961 from rohitwaghchaure/merge-13-hotfix-to-pre-release-for-13-13
chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release'
2021-10-14 19:17:22 +05:30
Rohit Waghchaure
952c60b3f5 Merge branch 'version-13-hotfix' into 'version-13-pre-release' 2021-10-14 18:31:09 +05:30
mergify[bot]
119c2f01e1 fix(perf): index creation on voucher_detail_no (#27866) (#27876)
voucher_detail_no is supposed to have an index, it was added on
on_doctype_update function of table, however this function is only
called if DocType itself is updated and `on_update` is called on
DocType. Stock ledger Entry doctype hasn't changed since addition of
this index in function.

Before: Lack of this index was causing full table scan in
get_future_sle_to_fix function. (~50 seconds in a reposting job)

After: Single row is fetched (~0.5 second in full reposting job)

Learnings:
1. Add simple indexes via doctype only
2. For complex indexes always change doctype.json file for it to take
   effect.

(cherry picked from commit 6019f60d0a)

Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
2021-10-08 17:19:34 +05:30
Rohit Waghchaure
d11c215f85 Merge branch 'version-13-pre-release' into version-13 2021-10-07 11:35:31 +05:30
Rohit Waghchaure
5ea5bfad3d bumped to version 13.12.1 2021-10-07 11:55:31 +05:50
Marica
cd87931cee Merge pull request #27833 from frappe/mergify/bp/version-13-pre-release/pr-27713
fix: Maintenance Schedule child table status for legacy data (backport #27713)
2021-10-06 14:08:15 +05:30
Marica
355ae1fca6 Merge branch 'version-13-pre-release' into mergify/bp/version-13-pre-release/pr-27713 2021-10-06 13:42:46 +05:30
mergify[bot]
91baa22d59 fix: remove stale doctypes and add msg for ecommerce refactor (bp #27700)
(cherry picked from commit 6d99bb5ce6)

Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
2021-10-06 13:37:26 +05:30
marination
af57e1e299 fix: reload doc in patch
(cherry picked from commit 6b38778dcb)
2021-10-06 07:57:22 +00:00
marination
1d3ba46107 fix: Add patch to patches.txt
(cherry picked from commit 7c47f36a4c)
2021-10-06 07:57:22 +00:00
Marica
65a590ced1 fix: Maintenance Schedule child table status for legacy data (#27554)
* fix: Maintenance Schedule child table status for legacy data

* fix: Include legacy draft schedules in patch

* fix: Pre-commit formatting

(cherry picked from commit cc143bca0d)
(cherry picked from commit 6ce2111b6d)
2021-10-06 07:57:21 +00:00
Marica
1a68e10742 Merge pull request #27829 from frappe/mergify/bp/version-13-pre-release/pr-27715
fix: Batch scans get overwritten on the same row (backport #27668) (backport #27715)
2021-10-06 12:57:39 +05:30
mergify[bot]
4fe827e86b Merge pull request #27715 from frappe/mergify/bp/version-13-hotfix/pr-27668
fix: Batch scans get overwritten on the same row (backport #27668)
(cherry picked from commit 15c9c08261)
2021-10-06 07:13:00 +00:00
Deepesh Garg
f573840c9f Merge pull request #27801 from frappe/mergify/bp/version-13-pre-release/pr-27524
fix: Tax breakup based on items, missing GST fields (backport #27524)
2021-10-06 09:33:30 +05:30
Rohit Waghchaure
0bc83deca0 Merge branch 'version-13-pre-release' into version-13 2021-10-06 00:26:31 +05:30
Rohit Waghchaure
696a01765b bumped to version 13.12.0 2021-10-06 00:46:31 +05:50
rohitwaghchaure
047debffeb Merge pull request #27825 from rohitwaghchaure/v13_12_0-change-log
chore: change log for v13_12_0
2021-10-06 00:02:19 +05:30
Rohit Waghchaure
633cd0c1a8 chore: change log for v13_12_0 2021-10-05 23:44:32 +05:30
Deepesh Garg
51b2fcc8ef fix: Update setup.py 2021-10-05 21:05:12 +05:30
Deepesh Garg
ff062d501f fix: Update patches.txt 2021-10-05 21:03:40 +05:30
mergify[bot]
89e421a01d fix: removed redundant piece of code (#27817) (#27822)
(cherry picked from commit cec66d2d10)

Co-authored-by: Afshan <33727827+AfshanKhan@users.noreply.github.com>
2021-10-05 19:24:40 +05:30
Noah Jacob
287a7b29e1 Merge pull request #27815 from frappe/mergify/bp/version-13-pre-release/pr-27813
fix: ignore random periodicity in validations (backport #27813)
2021-10-05 17:32:09 +05:30
Ankush Menat
a09647b3c8 fix: ignore random periodicity in validations
(cherry picked from commit 3d3655ed73)
2021-10-05 11:38:06 +00:00
Deepesh Garg
c1c5aa7262 Merge pull request #27810 from frappe/mergify/bp/version-13-pre-release/pr-27792
fix: COA Importer showing blank validations (backport #27792)
2021-10-05 15:39:29 +05:30
Deepesh Garg
b4f06c0d77 fix: Use get_list instead of get_all to avoid perm issues
(cherry picked from commit 9507b2d752)
2021-10-05 09:48:42 +00:00
Deepesh Garg
758939eddb fix: COA Importer showing blank validations
(cherry picked from commit 0660d6ed01)
2021-10-05 09:48:42 +00:00
mergify[bot]
fa944382c5 fix: using DN for transfer w/o internal customer (backport #27798) (#27805)
* fix: using DN for transfer w/o internal customer (#27798)

This used to be work before though not "advertised", since a lot of
users have started using it as feature, it can't be broken now.

(cherry picked from commit df1f8fddf6)

* fix(ux): use toast instead of popup

Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
2021-10-05 14:53:26 +05:30
Subin Tom
b1244df045 fix: Tax breakup based on items, missing GST fields (#27524)
* fix: Tax breakup based on items

* fix: added gst fields,warehouse validation to pos inv,patch

* fix: tax breakup test fix, eway bill hsn fix

Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
(cherry picked from commit d49346ac45)

# Conflicts:
#	erpnext/patches.txt
#	erpnext/regional/india/setup.py
2021-10-05 08:44:31 +00:00
Deepesh Garg
28aa9a7215 Merge pull request #27761 from frappe/mergify/bp/version-13-pre-release/pr-27712
fix(India): Internal transfer check fix (backport #27712)
2021-10-05 14:10:57 +05:30
Saqib
953073bc77 fix(asset): expected value after useful life validation (#27787) 2021-10-05 11:13:50 +05:30
Deepesh Garg
e6909a0899 fix(India): Internal transfer check fix
(cherry picked from commit f0af24fc6d)
2021-10-04 05:31:59 +00:00
Marica
68cce8eb98 Merge pull request #27757 from frappe/mergify/bp/version-13-pre-release/pr-27720
fix: Website Items with same Item name unhandled, thumbnails missing (backport #27720)
2021-10-03 14:46:18 +05:30
Marica
869c1beb0a Merge pull request #27744 from frappe/mergify/bp/version-13-pre-release/pr-27611
fix: Hero Slider Control & Alignment fixes (backport #27611)
2021-10-03 14:29:39 +05:30
marination
a453fc6385 fix: Pre-commit formatting
(cherry picked from commit 77d4849ce8)
2021-10-03 08:57:13 +00:00
marination
00978a195a fix: Website Items with same Item name unhandled, thumbnails missing
- Use naming series for Website Item. There could be two items with same name and different item code
- Fix: Website Item Page view breaks if cart is disabled
- Fix: thumbnails not created for Website Items after patch
- Fix: ‘Request for Quote’ button & cart summary not visible if checkout is disabled

(cherry picked from commit 36b519c962)
2021-10-03 08:57:12 +00:00
Deepesh Garg
66286881f3 Merge pull request #27752 from frappe/mergify/bp/version-13-pre-release/pr-27748
fix: Chart Of Accounts import button not visible (backport #27748)
2021-10-02 22:16:15 +05:30
Deepesh Garg
c2e4bbe9cc fix: Linting issues
(cherry picked from commit ff570f48a0)
2021-10-02 16:10:41 +00:00
Deepesh Garg
02984551ce fix: Remove unwanted comments
(cherry picked from commit e4b89d2fcd)
2021-10-02 16:10:40 +00:00
Deepesh Garg
42f0a97d26 fix: Chart Of Accounts import button not visible
(cherry picked from commit 3529622a0d)
2021-10-02 16:10:39 +00:00
Shariq Ansari
477d36413b chore: linter fix
(cherry picked from commit 18918e1b4f)
2021-10-02 11:39:01 +00:00
Shariq Ansari
b5fc3076bf fix: Fixed alignment of Title, Subtitle, Action Button
(cherry picked from commit 0de735f20b)
2021-10-02 11:39:00 +00:00
Shariq Ansari
2c9162160a fix: Creating unique hash for slider id instead of slider name
(cherry picked from commit 3e8e6ac4e2)
2021-10-02 11:38:59 +00:00
mergify[bot]
3e7a029869 fix: option to limit reposting in certain timeslot (bp #27725)
(cherry picked from commit a04f9c904e)

Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
2021-10-01 16:50:05 +05:30
mergify[bot]
f572a4e0e5 fix(Org Chart): use attribute selectors instead of ID selector for node IDs with special characters (#27717) (#27719)
* fix(Org Chart): use attribute selectors instead of ID selector for node IDs with special chars

* fix: UI tests

(cherry picked from commit 9e08229b7b)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2021-09-30 18:44:55 +05:30
rohitwaghchaure
348f082490 Merge pull request #27716 from rohitwaghchaure/merge-v13-hotfix-to-pre-release
chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release'
2021-09-30 18:07:47 +05:30
Rohit Waghchaure
49174cd414 chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release' 2021-09-30 16:59:18 +05:30
Rohit Waghchaure
c800ff5068 Merge branch 'version-13-pre-release' into version-13 2021-09-19 14:39:45 +05:30
Rohit Waghchaure
de8b3570f5 bumped to version 13.11.1 2021-09-19 14:59:45 +05:50
Frappe PR Bot
5978286b52 fix: Handle is_search_module_loaded for redis version < 4.0.0 (#27574) (#27578)
- Return False if error occurs

(cherry picked from commit d6ed6d53e9)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2021-09-18 15:25:44 +05:30
Frappe PR Bot
a741fd1cfe fix: PO/PINV - Check if doctype has company_address field before setting the value (#27441) (#27575)
Co-authored-by: Vama Mehta <vama.mehta@inqubit.in>
(cherry picked from commit 666eaae6ce)

Co-authored-by: vama <vamagithub@gmail.com>
2021-09-18 13:24:33 +05:30
Frappe PR Bot
bd1c823aa6 fix: unecessary keyword args were passed in mapper functions (#27563) (#27564)
(cherry picked from commit e03d9aa889)

Co-authored-by: Saqib <nextchamp.saqib@gmail.com>
2021-09-17 13:21:33 +05:30
Rohit Waghchaure
79d1cf161e Merge branch 'version-13-pre-release' into version-13 2021-09-15 21:11:53 +05:30
Rohit Waghchaure
fb55b57f5c bumped to version 13.11.0 2021-09-15 21:31:52 +05:50
Frappe PR Bot
4c51002cb2 fix: not able to submit stock entry with 350 items (#27523) (#27525)
(cherry picked from commit e6a1ad8016)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2021-09-15 21:06:31 +05:30
rohitwaghchaure
abd3aee5b5 chore: change log for version 13.11.0 (#27527) 2021-09-15 21:06:13 +05:30
Frappe PR Bot
6d2d97bac4 fix(minor): Remove b2c limit check from CDNR Invoices (#27516) (#27519)
* fix(minor): Remove b2c limit check from CDNR Invoices

* fix: Remove unnecessary format

(cherry picked from commit 978028c880)

Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
2021-09-15 19:11:28 +05:30
Frappe PR Bot
05663268e6 feat: provision to add scrap item in job card (#27483) (#27518)
(cherry picked from commit c5a77f60ed)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2021-09-15 18:15:38 +05:30
Frappe PR Bot
d1a4761955 fix: Maintain same rate in Stock Ledger until stock become positive (#27227) (#27477)
* fix: Maintain same rate in Stock Ledger until stock become positive

* fix: Maintain same rate in Stock Ledger until stock become positive

(cherry picked from commit 10754831c3)

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
2021-09-15 17:08:41 +05:30
Marica
9adb4c8b69 Merge pull request #27509 from frappe-pr-bot/backport/version-13-pre-release/27508
fix: Shopping Cart and Variant Selection
2021-09-15 14:17:53 +05:30
Marica
0572c0ae3e Merge pull request #27508 from marination/shopping-cart-fixes
fix: Shopping Cart and Variant Selection
(cherry picked from commit 9e0fb74ab2)
2021-09-15 08:31:20 +00:00
Frappe PR Bot
397fad7eb2 fix: Values with same account and different account number in consolidated balance sheet report (#27493) (#27503)
(cherry picked from commit 625626b973)

Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
2021-09-15 11:49:04 +05:30
Frappe PR Bot
2ae48eeac8 fix: Patch for updating tax withholding category dates (#27489) (#27494)
(cherry picked from commit c53b78e712)

Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
2021-09-15 10:29:50 +05:30
Frappe PR Bot
9659acb31e fix: calculate operating cost based on BOM Quantity (#27464) (#27500)
* fix: calculate operating cost based on BOM Quantity

* fix: added test cases

(cherry picked from commit 2e2985e4f1)

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2021-09-15 01:19:15 +05:30
Marica
a3d64c6cb9 Merge pull request #27490 from frappe-pr-bot/backport/version-13-pre-release/27488
fix: Args missing error on changing Price List currency with cart enabled
2021-09-14 19:12:04 +05:30
Marica
52157cc000 Merge pull request #27488 from marination/validate-cart-settings
fix: Args missing error on changing Price List currency with cart enabled
(cherry picked from commit 2a9fbc609d)
2021-09-14 13:14:20 +00:00
Marica
e6771f43a7 Merge pull request #27487 from frappe-pr-bot/backport/version-13-pre-release/27486
fix: Handle Excess/Multiple Item Transfer against Job Card
2021-09-14 18:25:02 +05:30
Marica
cf2d1681d8 Merge pull request #27486 from marination/job-card-excess-transfer-hotfix
fix: Handle Excess/Multiple Item Transfer against Job Card
(cherry picked from commit d76e5dcb93)
2021-09-14 12:31:06 +00:00
Frappe PR Bot
3c3f0adbd9 Merge pull request #27481 from deepeshgarg007/gstin_filter_issue_v13 (#27484)
fix: GSTR-1 Reports not showing any data
(cherry picked from commit d5f4160260)

Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
2021-09-14 17:35:02 +05:30
Deepesh Garg
7112c6b9d3 Merge pull request #27476 from deepeshgarg007/tds_dates_v13
feat: Validity dates in Tax Withholding Rates
2021-09-14 16:14:36 +05:30
Deepesh Garg
e8411e8bc2 fix: Update fiscal year 2021-09-14 15:32:12 +05:30
Deepesh Garg
a3db15ccb7 fix: Linting Issues 2021-09-14 12:54:18 +05:30
Deepesh Garg
9fda447dd9 fix: Test Case 2021-09-14 12:54:18 +05:30
Deepesh Garg
0b296e2190 fix: Hardcode fiscal year and posting date 2021-09-14 12:29:00 +05:30
Deepesh Garg
c161daa4be fix: Debug CI 2021-09-14 12:29:00 +05:30
Deepesh Garg
1fa4962723 test: Update test case 2021-09-14 12:28:41 +05:30
Deepesh Garg
9c35e3aa89 fix: Linting and patch fixes 2021-09-14 12:28:41 +05:30
Deepesh Garg
86220e9ed6 fix: Advance TDS test case 2021-09-14 12:28:41 +05:30
Deepesh Garg
b86454e7f4 feat: Validity dates in Tax Withholding Rates 2021-09-14 12:28:36 +05:30
Frappe PR Bot
3887a67f7e fix: editable price list rate field in sales transactions (#27455) (#27460)
(cherry picked from commit a5baf909b7)

Co-authored-by: Saqib <nextchamp.saqib@gmail.com>
2021-09-13 18:48:32 +05:30
Frappe PR Bot
dc5f7a0c09 fix(Payroll): incorrect component amount calculation if dependent on another payment days based component (#27454)
* fix(Payroll): incorrect component amount calculation if dependent on another payment days based component (#27349)

* fix(Payroll): incorrect component amount calculation if dependent on another payment days based component

* fix: set component amount precision at the end

* fix: consider default amount during taxt calculations

* test: component amount dependent on another payment days based component

* fix: test

(cherry picked from commit bab644a249)

# Conflicts:
#	erpnext/payroll/doctype/salary_slip/test_salary_slip.py

* fix: conflicts in test file

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2021-09-13 15:14:28 +05:30
Saqib
72d1cf0537 feat: (get_items_from) filter material request item in purchase order (#27452) 2021-09-13 14:01:39 +05:30
Frappe PR Bot
e39db1abe3 revert: "fix: Salary component account filter (#26605)" (#27446) (#27447)
This reverts commit aaea5edbdb.

(cherry picked from commit 5c1f0c98f8)

Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
2021-09-13 10:33:28 +05:30
Ankush Menat
71e4230ab0 chore: whitespace/imports 2021-09-13 10:31:00 +05:30
rohitwaghchaure
75e108ab81 Merge pull request #27423 from rohitwaghchaure/merge-hotfix-to-pre-release-v13-11-0
chore: Merge branch 'version-13-hotfix' into 'version-13-pre-release' for v13.11.0
2021-09-10 14:17:12 +05:30
Rohit Waghchaure
135e3b0f09 fix: sider issues 2021-09-10 13:00:30 +05:30
Rohit Waghchaure
c25bc3403d Merge branch 'version-13-hotfix' into merge-hotfix-to-pre-release-v13-11-0 2021-09-10 12:41:54 +05:30
Rohit Waghchaure
210fc4481a Merge branch 'version-13-pre-release' into version-13 2021-09-07 13:05:13 +05:30
Rohit Waghchaure
4f3e2240b8 bumped to version 13.10.2 2021-09-07 13:25:13 +05:50
Frappe PR Bot
ede188d138 fix: missed to add voucher_type, voucher_no to get GL Entries (#27377)
* fix: missed to add voucher_type, voucher_no to get GL Entries (#27368)

* fix: missed to add voucher_type, voucher_no to get gl entries

* test: get voucherwise details utilities

# Conflicts:
#	erpnext/accounts/test/test_utils.py

* fix: resolve conflicts

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
Co-authored-by: Ankush Menat <ankush@iwebnotes.com>

(cherry picked from commit 058d98342a)
2021-09-07 13:00:40 +05:30
Rohit Waghchaure
f28cb55d0f Merge branch 'version-13-pre-release' into version-13 2021-09-06 23:33:19 +05:30
Rohit Waghchaure
adb07ebe09 bumped to version 13.10.1 2021-09-06 23:53:19 +05:50
Frappe PR Bot
2565b1fb33 fix: patch failure for vat audit report (#27355) (#27356)
(cherry picked from commit 14b01619de)

Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
2021-09-06 13:51:27 +05:30
Frappe PR Bot
96aee284d2 fix: south africa vat patch failure (#27324)
* fix: south africa vat patch failure (#27323)

reload doc is necessary on new doctypes

(cherry picked from commit d1fe060e4a)

# Conflicts:
#	erpnext/patches/v13_0/add_custom_field_for_south_africa.py

* fix: resolve conflicts

Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
2021-09-03 12:29:27 +05:30
Rohit Waghchaure
6440e4f970 Merge branch 'version-13-pre-release' into version-13 2021-09-01 22:33:12 +05:30
Rohit Waghchaure
702eea3b54 bumped to version 13.10.0 2021-09-01 22:53:11 +05:50
rohitwaghchaure
e362e23941 Merge pull request #27303 from rohitwaghchaure/chnage-log-for-v13-10-0
chore: change log for v13.10.0
2021-09-01 22:27:45 +05:30
Rohit Waghchaure
68482b223f chore: change log for v13.10.0 2021-09-01 22:26:41 +05:30
Frappe PR Bot
c31bf155f0 fix: Healthcare Service Unit fixes (#27273) (#27274)
* fix: validate service unit setup against practitioner schedule

* fix: service unit properties getting overwritten

(cherry picked from commit ef76f62bc1)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2021-08-31 21:18:22 +05:30
Frappe PR Bot
d641dd68d4 fix: revert "refactor: simplify initialize_previous_data" (#27270) (#27271)
This reverts commit 2f5624e588.

(cherry picked from commit c1d986a0c6)

Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
2021-08-31 19:46:13 +05:30
Frappe PR Bot
155df936cd Revert "fix: add child item groups into the filters (#26997)" (#27266) (#27268)
This reverts commit c60d5523bc.

(cherry picked from commit 763450dcf8)

Co-authored-by: Afshan <33727827+AfshanKhan@users.noreply.github.com>
2021-08-31 19:13:04 +05:30
Frappe PR Bot
0c4f29edcf fix(minor): Incorrect unallocated amount on type receive (#27262) (#27263)
(cherry picked from commit c37cec9b9d)

Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
2021-08-31 18:53:30 +05:30
Frappe PR Bot
f20913fb69 fix: Correct company address not getting copied from Purchase Order to Invoice (#27217) (#27234)
* fix: Correct company adderess not getting copied from Purchase Order to Invoice

* fix: Linting issues

(cherry picked from commit fd467e6d32)

Co-authored-by: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com>
2021-08-31 11:08:58 +05:30
Frappe PR Bot
bafc9ddde4 fix: patches were breaking during migration (#27213) (#27241)
* fix: patches were breaking during migration (#27200)

* fix: patches were breaking during migrating

* fix: patches were breaking during migration

(cherry picked from commit 7433757489)

# Conflicts:
#	erpnext/patches.txt

* fix: resolve conflicts

Co-authored-by: Shadrak Gurupnor <30501401+shadrak98@users.noreply.github.com>
Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
(cherry picked from commit aa04051416)
2021-08-30 19:04:58 +05:30
Frappe PR Bot
d9b9888ad5 feat: (consistency) Add Primary Address and Contact section in Supplier (#27232) (#27233)
* feat: (consistency) Add Primary Address and Contact section in Supplier

- The same is present in customer and is inconsistent with supplier
- Helps quickly create primary address and contact via quick entry

* fix: Popup stale build and data consistency

- Include `supplier_quick_entry.js` in erpnext.bundle.js
- Create primary supplier address on update
- Set newly created address (quick entry)  in Supplier and Customer
- Clear address set in supplier and customer on delete (dependency)

* fix: Indentation and removed f-strings

- Sider: fixed indentation in js
- Dont use f-strings in queries

(cherry picked from commit 3d87d9f1d3)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2021-08-30 18:49:40 +05:30
Frappe PR Bot
03dcecff67 ci: use node action instead of apt (#27226) (#27237)
* ci: use node action instead of apt (#27220)

(cherry picked from commit e5e00700e5)

* ci: keep python version 3.6 for v13

* ci: use node v12

Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
(cherry picked from commit dc948cab3e)
2021-08-30 18:07:05 +05:30
Saqib
f71ff830ef fix: remove non-existent method call in hooks (#27224) 2021-08-30 11:49:43 +05:30
Frappe PR Bot
d88346c6cd fix: patches were breaking while migrating (#27205)
* fix: patches were breaking while migrating (#27195)

* fix: patches were breaking while migrating

* fix: Removed duplicate function

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
(cherry picked from commit 17e0fa7a8b)

# Conflicts:
#	erpnext/patches.txt

* fix: resolve conflicts

Co-authored-by: Shadrak Gurupnor <30501401+shadrak98@users.noreply.github.com>
Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
2021-08-28 14:53:59 +05:30
Frappe PR Bot
0767d2dac2 fix: v13 migration fails due to missing reload_doc (#27192) (#27194)
closes #25948

(cherry picked from commit 1eb2526d0b)

Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
2021-08-27 12:59:00 +05:30
Frappe PR Bot
64fab5b7d1 fix: operation time auto set to zero (#27190)
* fix: operation time auto set to zero (#27188)

(cherry picked from commit e6799d78ef)

# Conflicts:
#	erpnext/patches.txt

* fix: conflicts

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2021-08-27 11:02:44 +05:30
rohitwaghchaure
7c31e1f8bf chore: merge branch 'version-13-hotfix' into 'version-13-pre-release' (#27173)
* feat: add provision for process loss in manufac

* feat: add is process loss autoset and validation

* fix: add warehouse and unset is scrap for process loss items

* refactor: shift auto entry of is process loss check, update validations

* test: add bom tests for process loss val, add se test for qty calc

* fix: add more validations, remove source wh req for pl item

* fix: sider

* refactor: polyfill ??

* fix: sider

* refactor: validation error message formatting

* test: check manufacture completion qty in se and wo

* fix: wo tests, sider, account for pl in se validation

* fix: reword error messages, fix test values

* feat: add procss_loss_qty field in work order

* feat: process loss report, fix set pl query condition

* fix: correct value in test

* fix: get filters to work
- reorder and rename columns
- add work order filter

* fix: Shopping cart Exchange rate validation (#27050)

* fix: Shopping cart Exchange rate validation

- Use `get_exchange_rate` to check for price list exchange rate in cart settings
- Move cart exchange rate validation for Price List from hooks to doc event
- Call cart exchange rate validation on PL update only if PL is in cart and currency is changed

* chore: Comment out obsolete test

- Modifying this test means considering extreme edge cases, which seems pointless now

* fix: Remove snippet that got in due to cherry-pick from `develop`

- This snippet is not present in v13-hotfix. Via https://github.com/frappe/erpnext/pull/26520

Co-authored-by: Nabin Hait <nabinhait@gmail.com>

* feat: initialize party link for customer & suppliers

* feat: toggle to enable common party accounting

* feat: auto create advance entry on invoice submission

* test: creation of advance entry on invoice submission

* fix: remove unwanted filter query

* feat: validate multiple links

* fix: party link permissions

* perf: reduce number of queries to get party link

* fix: cost center & naming series

* fix: cost center in test_sales_invoice_against_supplier

* fix: Don't create inward SLE against SI unless is internal customer enabled (#27086)

* fix: Dont create inward SLE against SI unless is internal customer enabled

- Check if is internal customer enabled apart from target warehouse
- Test to check if inward SLE is made if target warehouse is accidentally set but customer is not internal

* test: Use internal customer for delivery of bundle items to target warehouse

- created `create_internal_customer` util
- reused it in delivery note and sales invoice tests
- use internal customer for target warehouse test in delivery note

(cherry picked from commit f4dc9ee2aa)

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

* fix: prevent over riding scrap table values, name kwargs, set currency

* fix(regional): minor fixes and test for South Africa VAT report (#26933) (#27162)

* fix: allow to change incoming rate manually in case of stand-alone credit note (#27164)

* fix: allow to change rate manually in case of stand-alone credit note (#27036)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
(cherry picked from commit fe4540d74d)

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

* fix: resolve conflicts

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
Co-authored-by: Ankush Menat <ankush@iwebnotes.com>

* fix: Fee Validity fixes (#27161)

* fix: Fee Validity fixes (#27156)

* chore: update Fee Validity form labels

* fix: first appointment should not be considered for Fee Validity

* fix: Fee Validity test cases

* fix: appointment test case

(cherry picked from commit 642b4c805c)

* fix: overlapping appointments

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>

* fix: Merge conflicts and place internal customer creation util in test_customer.py

* fix: internal customer util returns 'str' not doc object

* fix: negative qty validation on stock reco cancellation (#27170) (#27171)

* test: negative stock validation on SR cancel

* fix: negative stock setting ignored in stock reco

In stock reconcilation cancellation negative stock setting is ignored as
`db.get_value` is returning string `'0'` which is not casted to int/bool
for further logic. This causes negative qty, which evantually gets
caught by reposting but by design this should stop cancellation.

* test: typo and minor refactor

(cherry picked from commit e7109c18db)

Co-authored-by: Ankush Menat <ankush@iwebnotes.com>

Co-authored-by: 18alantom <2.alan.tom@gmail.com>
Co-authored-by: Marica <maricadsouza221197@gmail.com>
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
Co-authored-by: Saqib Ansari <nextchamp.saqib@gmail.com>
Co-authored-by: Frappe PR Bot <frappe.pr.bot@gmail.com>
Co-authored-by: Ankush Menat <ankush@iwebnotes.com>
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2021-08-26 19:49:27 +05:30
rohitwaghchaure
409cc95b7b Merge pull request #27158 from rohitwaghchaure/merge-branch-hotfix-to-pre-release-for-13-10
chore: merge version-13-hotfix into version-13-pre-release for release v13.10.0
2021-08-26 17:39:58 +05:30
Rohit Waghchaure
327be1cd9d chore: merge version-13-hotfix into version-13-pre-release for release v13.10.0 2021-08-26 11:45:55 +05:30
Rohit Waghchaure
23c713cc9b Merge branch 'version-13-pre-release' into version-13 2021-08-19 14:35:52 +05:30
Rohit Waghchaure
f0d3a074e0 bumped to version 13.9.2 2021-08-19 14:55:52 +05:50
rohitwaghchaure
cada9b679a Merge pull request #27019 from anupamvs/email-digest-fix-pre
fix: email digest recipient patch
2021-08-19 11:49:49 +05:30
Anupam Kumar
ecbb59a1ce Merge branch 'version-13-pre-release' into email-digest-fix-pre 2021-08-19 11:14:38 +05:30
Anupam
9f79415186 fix: email digest recipient patch 2021-08-19 11:12:32 +05:30
Rohit Waghchaure
8cd3ffc84d Merge branch 'version-13-pre-release' into version-13 2021-08-18 13:00:35 +05:30
Rohit Waghchaure
9c1d739946 bumped to version 13.9.1 2021-08-18 13:20:35 +05:50
Deepesh Garg
c8449702b4 Merge pull request #26981 from deepeshgarg007/payment_entry_unallocated_fix_v13_pre
fix: Incorrect unallocated amount calculation in payment entry
2021-08-17 19:13:27 +05:30
Deepesh Garg
0a5dff1e1f test: Add test case for payment entry 2021-08-17 18:11:29 +05:30
Deepesh Garg
e7143d8711 fix: Incorrect unallocated amount calculation in payment entry 2021-08-17 13:36:17 +05:30
Rohit Waghchaure
b27eeb54ae Merge branch 'version-13-pre-release' into version-13 2021-08-17 10:05:50 +05:30
Rohit Waghchaure
03fdce5a19 bumped to version 13.9.0 2021-08-17 10:25:49 +05:50
rohitwaghchaure
6dd9e0194c Merge pull request #26968 from rohitwaghchaure/release-notes-v13-9-0
chore: Release Notes v13.9.0
2021-08-16 22:17:47 +05:30
Rohit Waghchaure
9722641847 chore: Release Notes v13.9.0 2021-08-16 20:38:38 +05:30
Deepesh Garg
a320a8f1df Merge pull request #26966 from deepeshgarg007/distributed_budget_variance_report_v13_pre
fix: Budget variance missing values
2021-08-16 18:28:19 +05:30
Deepesh Garg
58c1739eac fix: Budget variance missing values 2021-08-16 18:04:57 +05:30
Deepesh Garg
3b4258b817 Merge pull request #26958 from deepeshgarg007/export_type_mandatory_v13_pre
fix: Add mandatory depends on condition for export type field
2021-08-16 15:16:07 +05:30
Deepesh Garg
4b9c127494 Merge branch 'version-13-pre-release' of https://github.com/frappe/erpnext into export_type_mandatory_v13_pre 2021-08-16 14:38:54 +05:30
Deepesh Garg
b58b1127f3 fix: Add mandatory depends on condition for export type field 2021-08-16 14:36:50 +05:30
Rucha Mahabal
67e3971c3b fix: Org Chart fixes (#26952)
* fix: add z-index to filter to avoid svg wrapper overlapping

* fix: expand all nodes not working when there are only 2 levels

- added dom freeze while expanding all nodes and exporting
2021-08-16 10:38:39 +05:30
Marica
f1c9766748 Merge pull request #26943 from frappe-pr-bot/backport/version-13-pre-release/26942
fix: (Stock Analytics) Copy previous balance dict object instead of assigning
2021-08-13 16:01:21 +05:30
Marica
2e6899fbe4 fix: Copy previous balance dict object instead of assigning (#26942)
- Due to plain assignment, dict mutation gave wrong monthly values

(cherry picked from commit fe2a34f171)
2021-08-13 10:08:41 +00:00
Frappe PR Bot
f4b2f4aaf7 fix: ZeroDivisionError on creating e-invoice for credit note (#26918) 2021-08-12 17:11:38 +05:30
Marica
b1406dba98 Merge pull request #26910 from frappe-pr-bot/backport/version-13-pre-release/26908
fix: Stock Analytics Report must consider warehouse during calculation
2021-08-12 10:52:20 +05:30
Marica
08e4026456 fix: Stock Analytics Report must consider warehouse during calculation (#26908)
* fix: Stock Analytics Report must consider warehouse during calculation

* fix: Brand filter in Stock Analytics

(cherry picked from commit 703b081172)
2021-08-12 05:02:42 +00:00
Suraj Shetty
fd1c47ef77 Merge pull request #26904 from surajshetty3416/fix-list-view 2021-08-11 14:53:58 +05:30
Suraj Shetty
99658ceb4e fix: Nest .level class style under .hierarchy class
- To avoid style overrides in list view
2021-08-11 14:19:07 +05:30
rohitwaghchaure
a631ac605f Merge pull request #26892 from rohitwaghchaure/merged-v13-hotfix-into-pre-release
chore: merge version-13-hotfix into version-13-pre-release
2021-08-11 00:13:30 +05:30
Rucha Mahabal
9855bbb95e fix(style): apply svg container margin only in desktop view (#26894) 2021-08-10 23:53:02 +05:30
Rohit Waghchaure
7167fd00bc Merge branch 'version-13-hotfix' into merged-v13-hotfix-into-pre-release 2021-08-10 23:30:16 +05:30
Frappe PR Bot
b7b111c3ed fix: unseting of payment if no pos profile found (#26884) (#26890)
(cherry picked from commit b614834efe)

Co-authored-by: Afshan <33727827+AfshanKhan@users.noreply.github.com>
2021-08-10 22:52:37 +05:30
rohitwaghchaure
7ca1493655 Merge pull request #26868 from rohitwaghchaure/merge-version-13-hotfix-into-v13-pre-9
chore: merge version-13-hotfix into v13-pre-release
2021-08-10 01:29:00 +05:30
Rohit Waghchaure
f571e42aa4 Merge branch 'version-13-hotfix' into merge-version-13-hotfix-into-v13-pre-9 2021-08-10 01:06:23 +05:30
Rohit Waghchaure
8880d13227 Merge branch 'version-13-pre-release' into version-13 2021-08-01 14:35:10 +05:30
Rohit Waghchaure
8cb560c753 bumped to version 13.8.0 2021-08-01 14:55:09 +05:50
rohitwaghchaure
b86213c890 Merge pull request #26753 from rohitwaghchaure/change-log-v13-8-0
chore: Added change log for v13.8.0
2021-08-01 11:37:05 +05:30
Rohit Waghchaure
6fffc90b46 chore: Added change log for v13.8.0 2021-07-31 16:22:12 +05:30
Deepesh Garg
64267faa8f Merge pull request #26742 from deepeshgarg007/tds_194q_calculations_v13
fix: TDS calculation for first threshold breach for TDS category 194Q
2021-07-30 20:41:37 +05:30
Deepesh Garg
6eded547f5 fix: TDS calculation for first threshold breach for TDS category 194Q 2021-07-30 20:12:51 +05:30
Saqib
d4ae1febe3 fix: gl entries for exchange gain loss (#26734) 2021-07-30 11:21:49 +05:30
rohitwaghchaure
50b4b70589 Merge pull request #26727 from deepeshgarg007/pricing_rule_item_group_v13
fix: Parent condition in pricing rules
2021-07-29 20:06:47 +05:30
Deepesh Garg
9c7a9f3a13 fix: Parent condition in pricing rules 2021-07-29 20:02:49 +05:30
Ankush
57cd273f7c fix: empty "against account" in Purchase Receipt GLE bp #26712 (#26719)
* fix: correct field for GLE against account in PR

* fix: remove incorrect field check from reposting
2021-07-29 19:49:36 +05:30
rohitwaghchaure
020b8ae110 Merge pull request #26721 from rohitwaghchaure/skip-cancelled-entries-in-report-pre
fix: remove cancelled entries from Stock and Account Value comparison report
2021-07-29 19:18:58 +05:30
Rohit Waghchaure
a6d276a06f fix: remove cancelled entries from Stock and Account Value comparison report 2021-07-29 19:16:20 +05:30
Ankush
f8343890b9 feat: don't recompute taxes (#26695) 2021-07-29 11:09:34 +05:30
Deepesh Garg
c6fe601efa Merge pull request #26677 from deepeshgarg007/tds_enhancements_v13
feat: Enhancements in TDS
2021-07-28 20:58:42 +05:30
rohitwaghchaure
bfd824c56e Merge pull request #26691 from frappe-pr-bot/backport/version-13-pre-release/26689
fix(bom): remove manual permission checking
2021-07-28 18:01:58 +05:30
Deepesh Garg
6ac68f3bc7 fix: Patch 2021-07-28 17:19:00 +05:30
Deepesh Garg
8c7d9efa9d fix: Chnage fieldtype from data to check 2021-07-28 17:18:50 +05:30
Ankush
8ed7a21cd5 fix(bom): remove manual permission checking (#26689)
get_list does the permission checking.

(cherry picked from commit d95f16ac8f)
2021-07-28 11:12:27 +00:00
Afshan
868a6cb26d fix: documentation link for E Invoicing (#26686) 2021-07-28 13:42:13 +05:30
Deepesh Garg
441adf763f fix(minor): Consider grand total for threshold check 2021-07-28 10:51:50 +05:30
Deepesh Garg
5a7fad8a6a feat: Enhancements in TDS 2021-07-27 18:55:14 +05:30
rohitwaghchaure
82d0cfc68e Merge pull request #26676 from rohitwaghchaure/fixed-dict-object-has-no-attr-pre
fix: not able to add employee in the job card
2021-07-27 18:46:59 +05:30
Rohit Waghchaure
940356d28a fix: not able to add employee in the job card 2021-07-27 18:45:17 +05:30
rohitwaghchaure
a07423ebb5 Merge pull request #26672 from frappe-pr-bot/backport/version-13-pre-release/26641
fix: reload manufacturing setting before patch
2021-07-27 17:28:53 +05:30
Ankush
7a97b6d6a8 fix: reload manufacturing setting before patch (#26641)
(cherry picked from commit c8d7a8c781)
2021-07-27 11:19:33 +00:00
Deepesh Garg
9d8efbe1db Merge pull request #26651 from deepeshgarg007/exchange_rate_reval_fixes_v13_pre_release
fix: Exchange rate revaluation posting date and precision fixes
2021-07-27 16:41:59 +05:30
Deepesh Garg
831ce88d1c Merge pull request #26643 from nemesis189/ignore-mandatory-in-payment-reconcilitation-pre-release
fix:Ignore mandatory fields while creating payment reconciliation Journal Entry
2021-07-27 10:16:04 +05:30
Deepesh Garg
af84ce9216 Merge pull request #26647 from deepeshgarg007/gst_sales_register_fix_v13
fix(India): Default value for export type
2021-07-27 10:12:33 +05:30
Deepesh Garg
61d014d249 Merge pull request #26644 from deepeshgarg007/gstr_3b_missing_cess_v13
fix: Add missing cess amount in GSTR-3B report
2021-07-26 19:20:50 +05:30
Deepesh Garg
93dd238577 Merge pull request #26646 from deepeshgarg007/gst_reports_timeout_v13
fix: GST Reports timeout issue
2021-07-26 19:19:34 +05:30
Deepesh Garg
3fcc5e3134 fix: Convert null values to empty string on grouping 2021-07-26 19:09:09 +05:30
Deepesh Garg
19a0ca1980 fix: Ignore GL Entry on cancel 2021-07-26 19:08:56 +05:30
Deepesh Garg
356a55258e fix: Exchange rate revaluation posting date and precision fixes 2021-07-26 19:08:45 +05:30
Deepesh Garg
cba847b051 fix: Test case for GSTR-3b report 2021-07-26 18:46:27 +05:30
Deepesh Garg
a661667e2a fix(India): Default value for export type 2021-07-26 18:46:14 +05:30
Deepesh Garg
5fe7d80a64 fix: GST Reports timeout issue 2021-07-26 17:13:28 +05:30
Deepesh Garg
c468e4a93d fix: Add missing cess amount in GSTR-3B report 2021-07-26 16:58:07 +05:30
Subin Tom
50b76d04bf fix:Ignore mandatory fields while creating payment reconciliation Journal Entry 2021-07-26 16:52:05 +05:30
Subin Tom
ed68f11a46 fix: Supplier invoice importer fix pre release (#26636)
* fix: Supplier Invoice Importer fix
Co-authored-by: Subin Tom <subin-home@Subins-MacBook-Air.local>
2021-07-26 16:47:36 +05:30
rohitwaghchaure
f3ae7584c5 Merge pull request #26635 from frappe-pr-bot/backport/version-13-pre-release/26576
fix: included company in link document type filters for contact
2021-07-26 13:08:33 +05:30
Noah Jacob
64454a5dc8 fix: included company in Link Document Type filters for contact (#26576)
(cherry picked from commit cbddedab7b)
2021-07-26 07:32:43 +00:00
rohitwaghchaure
7aa802a073 Merge pull request #26632 from rohitwaghchaure/merging-13-hotfix-to-13-pre-release-13-8
chore: Merging Version 13 hotfix to 13 Pre Release
2021-07-26 12:43:16 +05:30
Rohit Waghchaure
60a0b7fe77 Merge branch 'version-13-hotfix' into 'version-13-pre-release-13-8' 2021-07-26 11:34:57 +05:30
Nabin Hait
338f436a1c Merge branch 'version-13-pre-release' into version-13 2021-07-22 17:05:51 +05:30
Nabin Hait
7551bcf421 bumped to version 13.7.1 2021-07-22 17:25:51 +05:50
Nabin Hait
13466bba90 Merge pull request #26588 from deepeshgarg007/tax_flow_fix_pre_release
fix: Additional discount calculations in Invoices
2021-07-22 11:30:47 +05:30
Deepesh Garg
9ab18b5341 fix: add company change trigger 2021-07-21 23:18:17 +05:30
Deepesh Garg
9fa92c912b fix: Revert refresh field 2021-07-21 23:18:17 +05:30
Deepesh Garg
72eb72f66f fix: Add update item tax template method back 2021-07-21 23:18:17 +05:30
Deepesh Garg
50b188214d revert: Client side handling for Dynamic GST Rates 2021-07-21 23:18:17 +05:30
Deepesh Garg
f9da88cb15 fix: Additional discount calculations in Invoices 2021-07-21 23:17:56 +05:30
Nabin Hait
4e6f49209f Merge branch 'version-13-pre-release' into version-13 2021-07-16 15:21:33 +05:30
Nabin Hait
e079a1bb33 bumped to version 13.7.0 2021-07-16 15:41:33 +05:50
Nabin Hait
e8abb201c1 Merge pull request #26526 from rohitwaghchaure/patch-to-fix-missing-fg-item-pre
fix: added patch to fix missing FG item
2021-07-16 15:05:16 +05:30
Rohit Waghchaure
0c6ca09e06 fix: added patch to fix missing FG item 2021-07-16 14:33:25 +05:30
Nabin Hait
f6d6897f34 Merge pull request #26525 from nabinhait/change-log-v13-7-0
chore: Added change log for v13.7.0
2021-07-16 13:09:31 +05:30
Nabin Hait
a0df79ee8c chore: Added change log for v13.7.0 2021-07-16 13:07:39 +05:30
Noah Jacob
2f775ee53c fix: WIP needs to be set before submit on skip_transfer (#26507) 2021-07-15 16:29:28 +05:30
rohitwaghchaure
a51e230f4a Merge pull request #26509 from rohitwaghchaure/fixed-fg-items-not-added-for-batch-item-pre
fix: FG item not fetched in manufacture entry
2021-07-15 15:32:59 +05:30
Rohit Waghchaure
9997cce1ea fix: FG item not fetched in manufacture entry 2021-07-15 15:32:21 +05:30
Deepesh Garg
cc73e7d6fa Merge pull request #26472 from deepeshgarg007/payment_entry_taxes_unallocated_amount_v13
fix: Unallocated amount in Payment Entry after taxes
2021-07-15 12:38:45 +05:30
Marica
f225196028 Merge pull request #26497 from marination/paging-btns-pre-release
fix: Paging buttons not working on item group portal page
2021-07-14 21:19:59 +05:30
marination
f40da4ac4c fix: Paging buttons not working on item group portal page 2021-07-14 20:48:06 +05:30
Marica
683d18dd95 Merge pull request #26493 from 18alantom/bp-pr-fix-item-group-portal-issues
fix: item group portal issues (backport)
2021-07-14 18:01:19 +05:30
18alantom
9ea5072c52 fix: set item group as a persistent filter 2021-07-14 17:41:38 +05:30
18alantom
b24c0bfbf9 fix: show child item group items on portal 2021-07-14 17:41:32 +05:30
Noah Jacob
03f4db0606 fix: validation check for batch for stock reconciliation type in stock entry(bp #26370 ) (#26488)
* fix(ux): added filter for valid batch nos.

* fix: not validating batch no if entry type stock reconciliation

* test: validate batch_no
2021-07-14 16:28:54 +05:30
Saqib
72715956f1 fix: tds computation summary shows cancelled invoices (#26486) 2021-07-14 15:56:20 +05:30
Saqib
59bf2df28e fix: pos item cart dom updates (#26461) 2021-07-14 11:40:58 +05:30
Deepesh Garg
7ff6865817 Merge pull request #26470 from deepeshgarg007/new_company_error_v13
fix: Errors on parallel requests creation of company for India
2021-07-13 20:30:06 +05:30
Deepesh Garg
c00d851a88 fix: Unallocated amount for inclusive charges 2021-07-13 16:42:21 +05:30
Deepesh Garg
740d5c6c53 fix: Deduct included taxes from unallocated amount 2021-07-13 16:42:10 +05:30
Deepesh Garg
01c89eaad9 fix: Remove unintentional changes 2021-07-13 16:42:10 +05:30
Deepesh Garg
2f350bf450 fix: Remove unintentional changes 2021-07-13 16:42:10 +05:30
Deepesh Garg
9513d61a50 fix: Hide amount after tax fields 2021-07-13 16:42:10 +05:30
Deepesh Garg
51ae46f0de fix: Unallocated amount in Payment Entry after taxes 2021-07-13 16:42:10 +05:30
Deepesh Garg
2f89b5b38e Merge pull request #26466 from deepeshgarg007/amended_doc_discount_v13
fix: Incorrect discount amount on amended document
2021-07-13 16:24:54 +05:30
Deepesh Garg
41c5357602 Merge pull request #26468 from deepeshgarg007/gstr_1_json_dpwnload_error_v13
fix: Unable to download GSTR-1 json
2021-07-13 16:17:52 +05:30
rohitwaghchaure
bd8699b8a9 Merge pull request #26458 from rohitwaghchaure/fixed-multi-currency-issue-pre
fix: multi-currency issue
2021-07-13 16:05:36 +05:30
Deepesh Garg
fea29ae8cb fix: Use update flag for company dependant fixtures 2021-07-13 16:05:05 +05:30
Deepesh Garg
7be9f8dab1 fix: Error on creation of company for India 2021-07-13 16:04:56 +05:30
Deepesh Garg
0bad696faf fix: Unable to download GSTR-1 json 2021-07-13 15:46:58 +05:30
Deepesh Garg
07d9f3f74b fix: Incorrect discount amount on amended document 2021-07-13 15:35:59 +05:30
Rohit Waghchaure
4acbeecbbe fix: multi-currency issue 2021-07-13 15:35:30 +05:30
Saqib
621927d9f9 fix: move the rename abbreviation job to long queue (#26462) 2021-07-13 14:13:39 +05:30
Marica
fad426fd5c Merge pull request #26454 from noahjacob/supplier_defaults_v13pr
feat: fetching details from supplier/customer groups
2021-07-13 11:39:55 +05:30
Noah Jacob
74b3fc1e1c refactor: suggested changes 2021-07-13 11:05:53 +05:30
Noah Jacob
1e8f598ba5 fix: Sider 2021-07-13 11:05:53 +05:30
Noah Jacob
dd0a8f20e2 test: updated test cases 2021-07-13 11:05:53 +05:30
Noah Jacob
fedee0e8da test: test cases for fetching customer group details 2021-07-13 11:05:53 +05:30
Noah Jacob
f07f7e9305 test: test case for fetching supplier group details 2021-07-13 11:05:53 +05:30
Noah Jacob
1cb2af00a8 feat: details fetched from customer group in customer 2021-07-13 11:05:53 +05:30
Noah Jacob
9965af166e feat: details fetched from supplier group in supplier 2021-07-13 11:05:53 +05:30
Nabin Hait
1ef1daf383 Merge pull request #26432 from nabinhait/reposting-optimization-v13-pre-release
refactor: Optimized code for reposting item valuation
2021-07-12 15:42:19 +05:30
Nabin Hait
0003938f2b refactor: Optimized code for reposting item valuation 2021-07-12 13:29:20 +05:30
Nabin Hait
01aada6c90 refactor: Optimized code for reposting item valuation 2021-07-12 13:28:19 +05:30
Noah Jacob
ae41b53cee fix: stock_rbnb not defined (#26354) 2021-07-12 12:51:38 +05:30
Nabin Hait
470c7e773f fix: merge conflict 2021-07-12 12:45:49 +05:30
Sagar Vora
ea2470d174 Merge pull request #26361 from ankush/backport/version-13-pre-release/pr-26354
fix: stock_rbnb not defined (#26354)
2021-07-07 11:59:37 +05:30
Noah Jacob
a01264dae7 fix: stock_rbnb not defined (#26354) 2021-07-07 11:07:19 +05:30
Nabin Hait
c2b5562a3f Merge branch 'version-13-pre-release' into version-13 2021-07-01 12:37:13 +05:30
Nabin Hait
eee03fcbab bumped to version 13.6.0 2021-07-01 12:57:13 +05:50
Nabin Hait
d7c4818a5d Merge pull request #26281 from rohitwaghchaure/fixed-update-cost-not-working-for-draft-bom-pre
fix: update cost not working in the draft BOM
2021-07-01 12:30:55 +05:30
Nabin Hait
9a4926bb0d Merge pull request #26280 from rohitwaghchaure/fixed-employee-selection-issue-in-payroll-entry-pre
fix: employee selection not working in payroll entry
2021-07-01 12:30:13 +05:30
Rohit Waghchaure
f99f872946 fix: update cost not working in the draft bom 2021-07-01 12:02:37 +05:30
Rohit Waghchaure
87b4e6ea32 fix: employee selection not working in payroll entry 2021-07-01 12:02:03 +05:30
Nabin Hait
cf4e29a604 chore: Added change log for v13.6.0 2021-06-30 20:27:43 +05:30
Anupam Kumar
4df54cc62c Merge pull request #26269 from anupamvs/payroll-iss-pre-release
fix: fetching employee in payroll entry
2021-06-30 17:22:17 +05:30
Anupam
8492bf040d fix: feating employee in payroll entry 2021-06-30 17:17:43 +05:30
Rucha Mahabal
1f10a99910 fix: Employee Inactive status implications (#26245) 2021-06-29 15:58:56 +05:30
rohitwaghchaure
9bcc402f41 Merge pull request #26230 from rohitwaghchaure/fixed-fetch-bom-items-in-stock-reco-pre
fix: fetch batch items in stock reconciliation
2021-06-28 15:18:07 +05:30
rohitwaghchaure
5abf905ff7 Merge pull request #26229 from rohitwaghchaure/fixed-removed-values-out-of-sync-on-stock-transaction-submission-pre
fix: removed values out of sync validation from stock transactions
2021-06-26 23:43:57 +05:30
Rohit Waghchaure
478360397d fix: fetch batch items in stock reco 2021-06-26 22:31:03 +05:30
Rohit Waghchaure
5d5dc56f94 fix: removed values out of sync validation from stock transactions 2021-06-26 22:29:19 +05:30
Ankush Menat
6588a936d5 fix: order and time of operations for multilevel bom
- Order of operations was being sorted by idx of individual operations in
BOM table, which made the ordering useless.
- This adds ordering that's sorted from lowest level item to top level
item.
- chore: remove dead functionality. There's no `items` table. Required
  item level operations get overwritten on fetching of items /
  operations e.g. when clicking on multi-level BOM checkbox.
- test: add test for tree representation
- feat: BOMTree class to get complete representation of a tree
2021-06-25 20:16:11 +05:30
Ankush Menat
9af3f12411 fix(ux): show bom in operations child table 2021-06-25 20:16:11 +05:30
Ankush Menat
b4e7ee0e45 chore: remove dead and py2 compatibility code
form_grid_template doesn't exist
2021-06-25 20:16:11 +05:30
Deepesh Garg
bb0b1e61d6 Merge pull request #26220 from deepeshgarg007/item_tax_error_fetch_v13
fix: Error while fetching item taxes
2021-06-25 14:03:14 +05:30
Deepesh Garg
6eb8d19cc9 fix: Check for is None 2021-06-25 14:02:12 +05:30
Deepesh Garg
cd36ba7e64 fix: Error while fetching item taxes 2021-06-25 14:02:04 +05:30
Ankush
532a224c44 fix: precision rate for packed items (#26046) (#26217)
Co-authored-by: Noah Jacob <noahjacobkurian@gmail.com>
2021-06-25 13:28:01 +05:30
Nabin Hait
67768faaef Merge branch 'version-13-hotfix' into version-13-pre-release 2021-06-24 20:16:04 +05:30
Nabin Hait
a57976660a fix: merge conflict 2021-06-24 19:14:33 +05:30
Nabin Hait
4704003c94 Merge branch 'version-13-pre-release' into version-13 2021-06-24 15:35:50 +05:30
Nabin Hait
b5d1a7731c bumped to version 13.5.2 2021-06-24 15:55:50 +05:50
Nabin Hait
7a8a449ed4 Merge pull request #26197 from deepeshgarg007/deferred_revenue_error_pre_release
fix: Error while booking deferred revenue
2021-06-24 15:34:58 +05:30
Marica
bf3e191570 Merge pull request #26193 from marination/country-modal-cart-v13-release
fix: Address Card issues in e-commerce
2021-06-24 14:57:39 +05:30
Marica
b5ffaff6a8 Merge pull request #26194 from marination/web-item-group-v13-pre-release
fix: Website item group logic for product listing in Item Group pages
2021-06-24 14:57:29 +05:30
Deepesh Garg
f79a72dbf3 fix: Error while booking deferred revenue 2021-06-24 14:49:09 +05:30
marination
820a579051 chore: Test for Item visibility in multiple item group pages 2021-06-24 13:57:18 +05:30
marination
4f0e6cd911 fix: Sider 2021-06-24 13:57:00 +05:30
marination
f913838373 fix: Consider Table Multiselect fields in Query engine
- Since table multiselect fields were not handled, the query tried searching for this child field in item master
- This broke the query
- On trying to reload or go back to all-products page with field filters that are table mutiselect, page breaks
2021-06-24 13:56:49 +05:30
marination
9f305e983c fix: Filters did not consider Website Item Group 2021-06-24 13:56:35 +05:30
marination
ea2408744a fix: Consider Website Item Groups in Item group page product listing
- Passed an argument to query engine to know when query is for item group page
- If for item group page, get data with regards to website item group table
- This query should be fast since there's one filter and that shortens the table beforehand
- This data is merged with the results from the Item master (results only considering item attributes and field filters)
- The combined data is then sorted as per weightage

Co-authored-by: Gavin D'souza <gavin18d@gmail.com>
2021-06-24 13:56:15 +05:30
marination
5884f1aeb0 fix: (style) Address card buttons hover state 2021-06-24 13:50:29 +05:30
marination
26bec9d7b4 fix: Country Link field in 'Add address' website modal auto-clears 2021-06-24 13:50:18 +05:30
Nabin Hait
6495a4d2ed Merge branch 'version-13-pre-release' into version-13 2021-06-21 11:28:57 +05:30
Nabin Hait
94484d7766 bumped to version 13.5.1 2021-06-21 11:48:57 +05:50
Nabin Hait
9cab26b7bf Merge pull request #26123 from nextchamp-saqib/pos-change-gl-fix-pre-release
fix(pos): unsupported operand type -= for 'float' and 'NoneType'
2021-06-21 11:27:59 +05:30
Saqib Ansari
99531a35e0 fix(pos): unsupported operand type -= for 'float' and 'NoneType' 2021-06-21 10:47:23 +05:30
Nabin Hait
a97556fa8d Merge branch 'version-13-pre-release' into version-13 2021-06-15 19:58:40 +05:30
Nabin Hait
24a88f6cf6 bumped to version 13.5.0 2021-06-15 20:18:40 +05:50
Nabin Hait
1e2df2c109 fix(pos): 'NoneType' object is not iterable 2021-06-15 19:53:57 +05:30
Nabin Hait
b2f2d0e749 chore: Added change log for v13.5.0 2021-06-15 19:44:06 +05:30
Deepesh Garg
95bc141531 Merge pull request #26056 from deepeshgarg007/gst_taxtable_value_with_discount_v13
fix(India): Taxable value for invoices with additional discount
2021-06-15 12:05:16 +05:30
Deepesh Garg
433815daba fix: Update einvoice json test 2021-06-14 20:43:51 +05:30
Deepesh Garg
4afda3c89c fix(India): Taxable value for invoices with additional discount 2021-06-14 20:43:42 +05:30
Deepesh Garg
a717b5ad6e Merge pull request #26055 from deepeshgarg007/payment_entry_auto_tax_fixes_pre_release
fix: Auto tax calculations in Payment Entry
2021-06-14 20:39:52 +05:30
Deepesh Garg
bbf6121bb5 fix: Revert unintended changes 2021-06-14 20:09:31 +05:30
Deepesh Garg
ac52daa14f fix: Import throw 2021-06-14 20:09:23 +05:30
Deepesh Garg
5ef9a62917 fix: Add separate function to validate payment entry taxes 2021-06-14 20:09:17 +05:30
Deepesh Garg
302855e160 fix: Auto tax calculations in Payment Entry 2021-06-14 20:09:10 +05:30
Nabin Hait
c8ed863454 Merge pull request #26029 from ruchamahabal/add-inactive-status-to-employee-v13-release
feat: add Inactive status to Employee
2021-06-14 12:32:28 +05:30
Rucha Mahabal
efd7d584b2 feat: add Inactive status to Employee 2021-06-12 13:34:20 +05:30
Nabin Hait
42d72b55b8 Merge branch 'version-13-hotfix' into version-13-pre-release 2021-06-10 20:26:31 +05:30
Nabin Hait
27299cfcc5 Merge branch 'version-13-hotfix' into version-13-pre-release 2021-06-10 19:45:38 +05:30
rohitwaghchaure
cd503ce01d Merge pull request #25938 from rohitwaghchaure/fixed-timeout-error-in-repost-item-valuation-v13-pre
fix: timeout error in the repost item valuation
2021-06-03 22:14:01 +05:30
Raffael Meyer
cee0ddea78 Merge pull request #25943 from rohitwaghchaure/fixed-filters-for-item-query-pre
fix: filter type for item query
2021-06-03 15:58:20 +02:00
Rohit Waghchaure
8449a47048 fix: filter type for item query 2021-06-03 18:32:28 +05:30
Rohit Waghchaure
4e10ce1632 fix: timeout error in the repost item valuation 2021-06-03 16:35:59 +05:30
Nabin Hait
0385ae2b43 Merge branch 'version-13-pre-release' into version-13 2021-06-02 22:14:20 +05:30
Nabin Hait
8821d71094 bumped to version 13.4.1 2021-06-02 22:34:19 +05:50
Nabin Hait
830e87a4dd Merge pull request #25917 from rohitwaghchaure/fixed-work-order-no-able-to-select-item-pre
fix: not able to select the item code in work order
2021-06-02 18:59:41 +05:30
Rohit Waghchaure
5dd92934ae fix: not able to select the item code in work order 2021-06-02 14:27:24 +05:30
Nabin Hait
d0ba0217e4 Merge branch 'version-13-pre-release' into version-13 2021-05-31 11:14:33 +05:30
Nabin Hait
c63e233bc7 bumped to version 13.4.0 2021-05-31 11:34:33 +05:50
Nabin Hait
4c94ccc8d8 chore: Added change log 2021-05-31 10:51:57 +05:30
Ankush Menat
2df7f474fa fix: use dictionary filter instead of list (#25875)
Item query doesn't support list filter anymore.
2021-05-28 21:12:10 +05:30
Deepesh Garg
d4dc76c3ee Merge pull request #25867 from deepeshgarg007/itc_reversal_address_pre
fix(India): Show only company addresses for ITC reversal entry
2021-05-28 18:45:59 +05:30
Deepesh Garg
b084f1d320 fix(India): Show only company addresses for ITC reversal entry 2021-05-28 12:14:56 +05:30
Deepesh Garg
5269f1537d Merge pull request #25857 from Anuja-pawar/fix-ageing-error-pre-release
fix: ageing error in PSOA
2021-05-27 17:38:01 +05:30
Anuja P
753e5894de fix: ageing error in PSOA 2021-05-27 17:33:22 +05:30
Nabin Hait
e5df60287e Merge branch 'version-13-hotfix' into version-13-pre-release 2021-05-25 20:42:58 +05:30
Nabin Hait
db95db892c fix: merge conflict 2021-05-25 19:33:23 +05:30
Nabin Hait
8f057b4bac Merge branch 'version-13-pre-release' into version-13 2021-05-24 16:45:27 +05:30
Nabin Hait
10085580c8 bumped to version 13.3.1 2021-05-24 17:05:27 +05:50
Saqib
6368c976c7 fix: expected amount in pos closing payments table (#25737) 2021-05-24 16:44:38 +05:30
Nabin Hait
6bae78f410 Merge branch 'version-13-pre-release' into version-13 2021-05-17 11:36:29 +05:30
Nabin Hait
bc92ecb10f bumped to version 13.3.0 2021-05-17 11:56:29 +05:50
Nabin Hait
9ec0f11800 fix: renamed change log 2021-05-17 10:50:42 +05:30
Nabin Hait
2f403f1bcd fix: renamed change log 2021-05-17 10:50:26 +05:30
Nabin Hait
ad0b8fdd1e chore: Added change log for v13.3.0 2021-05-17 10:49:21 +05:30
rohitwaghchaure
e9f6c8cdb1 fix: validation message of quality inspection in purchase receipt (#25667) 2021-05-14 12:34:13 +05:30
rohitwaghchaure
0048418c46 Merge pull request #25703 from rohitwaghchaure/change-today-to-now-for-reposting-pre
fix: change today to now to get data for reposting
2021-05-13 17:44:27 +05:30
Rohit Waghchaure
a0a88a710e fix: change today to now to get data for reposting 2021-05-13 17:42:06 +05:30
Mohammad Hasnain Mohsin Rajan
7c6de1a8ac fix: bank statement import via google sheet (#25677)
* fix: change links in workspace

* fix: google sheet bank statement import

* chore: quotes

* fix: capitalization

* fix: typo

* chore: add translation
2021-05-13 17:28:49 +05:30
Deepesh Garg
6084baa9ae Merge pull request #25700 from deepeshgarg007/consolidated_report_param_fix_v13
fix: Parameter for get_filtered_list_for_consolidated_report in consolidated balance sheet
2021-05-13 15:23:53 +05:30
Deepesh Garg
8e748f8451 fix: Parameter for get_filtered_list_for_consolidated_report in consolidated balance sheet 2021-05-13 15:21:51 +05:30
rohitwaghchaure
c29c6ff9a7 Merge pull request #25692 from rohitwaghchaure/fixed-woocommerce-order-sync-issue-pre
fix: Woocommerce order sync issue
2021-05-12 23:09:33 +05:30
Rohit Waghchaure
fe68a0ff80 fix: Woocommerce order sync issue 2021-05-12 23:08:16 +05:30
Afshan
6578c045ca fix: Dialog variable assignment after definition in POS (#25680) 2021-05-12 17:41:50 +05:30
rohitwaghchaure
4ec0656f64 Merge pull request #25678 from rohitwaghchaure/fixed-new-fields-not-added-pre
fix: updated modified time in purchase invoice to pull new fields
2021-05-12 16:39:35 +05:30
Rohit Waghchaure
aaca8335f0 fix: updated modified time to pull new fields 2021-05-12 16:36:25 +05:30
Mohammad Hasnain Mohsin Rajan
dd1822ef58 fix: change links in workspace (#25673) 2021-05-12 13:01:53 +05:30
Nabin Hait
e38192cb6d fix: merge conflict 2021-05-11 11:15:09 +05:30
Nabin Hait
ec2ba6bc1f Merge branch 'version-13-pre-release' into version-13 2021-05-06 19:18:17 +05:30
Nabin Hait
ae18efaa0a bumped to version 13.2.1 2021-05-06 19:38:17 +05:50
Nabin Hait
0e41295c0e perf: Performance enhancement on setup wizard (#25606)
* perf: Performance enhancement on setup wizard

* fix: create departments without updating nsm
2021-05-06 19:14:06 +05:30
Saqib
92cefd5655 feat(pos): ability to retry on pos closing failure (#25604)
* feat(pos): ability to retry on pos closing failure

* fix: sider issues

* fix: sider issues

* fix: mark all queued closing entry as failed

* feat: add headline message
2021-05-06 17:03:16 +05:30
Deepesh Garg
daf6c124a9 fix: Check if payment schedule exists 2021-05-06 16:47:02 +05:30
Deepesh Garg
be66ee9723 fix: Check if payment schedule exits before updating label 2021-05-06 16:46:55 +05:30
Deepesh Garg
a0ed517c85 fix: function call to update payment schedule labels 2021-05-06 16:46:47 +05:30
1776 changed files with 255755 additions and 69157 deletions

View File

@@ -29,6 +29,8 @@ ignore =
B950,
W191,
E124, # closing bracket, irritating while writing QB code
E131, # continuation line unaligned for hanging indent
E123, # closing bracket does not match indentation of opening bracket's line
max-line-length = 200
exclude=.github/helper/semgrep_rules

View File

@@ -17,3 +17,6 @@ f0bcb753fb7ebbb64bb0d6906d431d002f0f7d8f
# imports cleanup
4b2be2999f2203493b49bf74c5b440d49e38b5e3
# formatting with black
c07713b860505211db2af685e2e950bf5dd7dd3a

View File

@@ -2,6 +2,13 @@
set -e
# Check for merge conflicts before proceeding
python -m compileall -f "${GITHUB_WORKSPACE}"
if grep -lr --exclude-dir=node_modules "^<<<<<<< " "${GITHUB_WORKSPACE}"
then echo "Found merge conflicts"
exit 1
fi
cd ~ || exit
sudo apt-get install redis-server libcups2-dev

View File

@@ -11,4 +11,4 @@ jobs:
- name: curl
run: |
apk add curl bash
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.com/repo/frappe%2Ffrappe_docker/requests
curl -X POST -H "Accept: application/vnd.github.v3+json" -H "Authorization: Bearer ${{ secrets.CI_PAT }}" https://api.github.com/repos/frappe/frappe_docker/actions/workflows/build_stable.yml/dispatches -d '{"ref":"main"}'

View File

@@ -5,7 +5,6 @@ on:
paths-ignore:
- '**.js'
- '**.md'
types: [opened, unlabeled, synchronize, reopened]
workflow_dispatch:
@@ -30,11 +29,6 @@ jobs:
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
steps:
- name: Check for merge conficts label
if: ${{ contains(github.event.pull_request.labels.*.name, 'conflicts') }}
run: |
echo "Remove merge conflicts and remove conflict label to run CI"
exit 1
- name: Clone
uses: actions/checkout@v2

31
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: Generate Semantic Release
on:
push:
branches:
- version-13
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout Entire Repository
uses: actions/checkout@v2
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js v14
uses: actions/setup-node@v2
with:
node-version: 14
- name: Setup dependencies
run: |
npm install @semantic-release/git @semantic-release/exec --no-save
- name: Create Release
env:
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
GIT_AUTHOR_NAME: "Frappe PR Bot"
GIT_AUTHOR_EMAIL: "developers@frappe.io"
GIT_COMMITTER_NAME: "Frappe PR Bot"
GIT_COMMITTER_EMAIL: "developers@frappe.io"
run: npx semantic-release

View File

@@ -5,7 +5,6 @@ on:
paths-ignore:
- '**.js'
- '**.md'
types: [opened, unlabeled, synchronize, reopened]
workflow_dispatch:
push:
branches: [ develop ]
@@ -40,12 +39,6 @@ jobs:
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
steps:
- name: Check for merge conficts label
if: ${{ contains(github.event.pull_request.labels.*.name, 'conflicts') }}
run: |
echo "Remove merge conflicts and remove conflict label to run CI"
exit 1
- name: Clone
uses: actions/checkout@v2

View File

@@ -26,12 +26,19 @@ repos:
args: ['--config', '.github/helper/.flake8_strict']
exclude: ".*setup.py$"
- repo: https://github.com/adityahase/black
rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119
hooks:
- id: black
additional_dependencies: ['click==8.0.4']
- repo: https://github.com/timothycrosley/isort
rev: 5.9.1
hooks:
- id: isort
exclude: ".*setup.py$"
ci:
autoupdate_schedule: weekly
skip: []

24
.releaserc Normal file
View File

@@ -0,0 +1,24 @@
{
"branches": ["version-13"],
"plugins": [
"@semantic-release/commit-analyzer", {
"preset": "angular",
"releaseRules": [
{"breaking": true, "release": false}
]
},
"@semantic-release/release-notes-generator",
[
"@semantic-release/exec", {
"prepareCmd": 'sed -ir "s/[0-9]*\.[0-9]*\.[0-9]*/${nextRelease.version}/" erpnext/__init__.py'
}
],
[
"@semantic-release/git", {
"assets": ["erpnext/__init__.py"],
"message": "chore(release): Bumped to Version ${nextRelease.version}\n\n${nextRelease.notes}"
}
],
"@semantic-release/github"
]
}

View File

@@ -1,53 +1,60 @@
import inspect
import frappe
from erpnext.hooks import regional_overrides
__version__ = '13.2.0'
__version__ = "13.32.0"
def get_default_company(user=None):
'''Get default company for user'''
"""Get default company for user"""
from frappe.defaults import get_user_default_as_list
if not user:
user = frappe.session.user
companies = get_user_default_as_list(user, 'company')
companies = get_user_default_as_list(user, "company")
if companies:
default_company = companies[0]
else:
default_company = frappe.db.get_single_value('Global Defaults', 'default_company')
default_company = frappe.db.get_single_value("Global Defaults", "default_company")
return default_company
def get_default_currency():
'''Returns the currency of the default company'''
"""Returns the currency of the default company"""
company = get_default_company()
if company:
return frappe.get_cached_value('Company', company, 'default_currency')
return frappe.get_cached_value("Company", company, "default_currency")
def get_default_cost_center(company):
'''Returns the default cost center of the company'''
"""Returns the default cost center of the company"""
if not company:
return None
if not frappe.flags.company_cost_center:
frappe.flags.company_cost_center = {}
if not company in frappe.flags.company_cost_center:
frappe.flags.company_cost_center[company] = frappe.get_cached_value('Company', company, 'cost_center')
frappe.flags.company_cost_center[company] = frappe.get_cached_value(
"Company", company, "cost_center"
)
return frappe.flags.company_cost_center[company]
def get_company_currency(company):
'''Returns the default company currency'''
"""Returns the default company currency"""
if not frappe.flags.company_currency:
frappe.flags.company_currency = {}
if not company in frappe.flags.company_currency:
frappe.flags.company_currency[company] = frappe.db.get_value('Company', company, 'default_currency', cache=True)
frappe.flags.company_currency[company] = frappe.db.get_value(
"Company", company, "default_currency", cache=True
)
return frappe.flags.company_currency[company]
def set_perpetual_inventory(enable=1, company=None):
if not company:
company = "_Test Company" if frappe.flags.in_test else get_default_company()
@@ -56,9 +63,10 @@ def set_perpetual_inventory(enable=1, company=None):
company.enable_perpetual_inventory = enable
company.save()
def encode_company_abbr(name, company=None, abbr=None):
'''Returns name encoded with company abbreviation'''
company_abbr = abbr or frappe.get_cached_value('Company', company, "abbr")
"""Returns name encoded with company abbreviation"""
company_abbr = abbr or frappe.get_cached_value("Company", company, "abbr")
parts = name.rsplit(" - ", 1)
if parts[-1].lower() != company_abbr.lower():
@@ -66,65 +74,73 @@ def encode_company_abbr(name, company=None, abbr=None):
return " - ".join(parts)
def is_perpetual_inventory_enabled(company):
if not company:
company = "_Test Company" if frappe.flags.in_test else get_default_company()
if not hasattr(frappe.local, 'enable_perpetual_inventory'):
if not hasattr(frappe.local, "enable_perpetual_inventory"):
frappe.local.enable_perpetual_inventory = {}
if not company in frappe.local.enable_perpetual_inventory:
frappe.local.enable_perpetual_inventory[company] = frappe.get_cached_value('Company',
company, "enable_perpetual_inventory") or 0
frappe.local.enable_perpetual_inventory[company] = (
frappe.get_cached_value("Company", company, "enable_perpetual_inventory") or 0
)
return frappe.local.enable_perpetual_inventory[company]
def get_default_finance_book(company=None):
if not company:
company = get_default_company()
if not hasattr(frappe.local, 'default_finance_book'):
if not hasattr(frappe.local, "default_finance_book"):
frappe.local.default_finance_book = {}
if not company in frappe.local.default_finance_book:
frappe.local.default_finance_book[company] = frappe.get_cached_value('Company',
company, "default_finance_book")
frappe.local.default_finance_book[company] = frappe.get_cached_value(
"Company", company, "default_finance_book"
)
return frappe.local.default_finance_book[company]
def get_party_account_type(party_type):
if not hasattr(frappe.local, 'party_account_types'):
if not hasattr(frappe.local, "party_account_types"):
frappe.local.party_account_types = {}
if not party_type in frappe.local.party_account_types:
frappe.local.party_account_types[party_type] = frappe.db.get_value("Party Type",
party_type, "account_type") or ''
frappe.local.party_account_types[party_type] = (
frappe.db.get_value("Party Type", party_type, "account_type") or ""
)
return frappe.local.party_account_types[party_type]
def get_region(company=None):
'''Return the default country based on flag, company or global settings
"""Return the default country based on flag, company or global settings
You can also set global company flag in `frappe.flags.company`
'''
"""
if company or frappe.flags.company:
return frappe.get_cached_value('Company',
company or frappe.flags.company, 'country')
return frappe.get_cached_value("Company", company or frappe.flags.company, "country")
elif frappe.flags.country:
return frappe.flags.country
else:
return frappe.get_system_settings('country')
return frappe.get_system_settings("country")
def allow_regional(fn):
'''Decorator to make a function regionally overridable
"""Decorator to make a function regionally overridable
Example:
@erpnext.allow_regional
def myfunction():
pass'''
pass"""
def caller(*args, **kwargs):
region = get_region()
fn_name = inspect.getmodule(fn).__name__ + '.' + fn.__name__
fn_name = inspect.getmodule(fn).__name__ + "." + fn.__name__
if region in regional_overrides and fn_name in regional_overrides[region]:
return frappe.get_attr(regional_overrides[region][fn_name])(*args, **kwargs)
else:
@@ -132,10 +148,16 @@ def allow_regional(fn):
return caller
def get_last_membership(member):
'''Returns last membership if exists'''
last_membership = frappe.get_all('Membership', 'name,to_date,membership_type',
dict(member=member, paid=1), order_by='to_date desc', limit=1)
"""Returns last membership if exists"""
last_membership = frappe.get_all(
"Membership",
"name,to_date,membership_type",
dict(member=member, paid=1),
order_by="to_date desc",
limit=1,
)
if last_membership:
return last_membership[0]

View File

@@ -23,29 +23,31 @@ class ERPNextAddress(Address):
if self.is_your_company_address and not [
row for row in self.links if row.link_doctype == "Company"
]:
frappe.throw(_("Address needs to be linked to a Company. Please add a row for Company in the Links table."),
title=_("Company Not Linked"))
frappe.throw(
_("Address needs to be linked to a Company. Please add a row for Company in the Links table."),
title=_("Company Not Linked"),
)
def on_update(self):
"""
After Address is updated, update the related 'Primary Address' on Customer.
"""
address_display = get_address_display(self.as_dict())
filters = { "customer_primary_address": self.name }
filters = {"customer_primary_address": self.name}
customers = frappe.db.get_all("Customer", filters=filters, as_list=True)
for customer_name in customers:
frappe.db.set_value("Customer", customer_name[0], "primary_address", address_display)
@frappe.whitelist()
def get_shipping_address(company, address = None):
def get_shipping_address(company, address=None):
filters = [
["Dynamic Link", "link_doctype", "=", "Company"],
["Dynamic Link", "link_name", "=", company],
["Address", "is_your_company_address", "=", 1]
["Address", "is_your_company_address", "=", 1],
]
fields = ["*"]
if address and frappe.db.get_value('Dynamic Link',
{'parent': address, 'link_name': company}):
if address and frappe.db.get_value("Dynamic Link", {"parent": address, "link_name": company}):
filters.append(["Address", "name", "=", address])
if not address:
filters.append(["Address", "is_shipping_address", "=", 1])

View File

@@ -12,15 +12,24 @@ from frappe.utils.nestedset import get_descendants_of
@frappe.whitelist()
@cache_source
def get(chart_name = None, chart = None, no_cache = None, filters = None, from_date = None,
to_date = None, timespan = None, time_interval = None, heatmap_year = None):
def get(
chart_name=None,
chart=None,
no_cache=None,
filters=None,
from_date=None,
to_date=None,
timespan=None,
time_interval=None,
heatmap_year=None,
):
if chart_name:
chart = frappe.get_doc('Dashboard Chart', chart_name)
chart = frappe.get_doc("Dashboard Chart", chart_name)
else:
chart = frappe._dict(frappe.parse_json(chart))
timespan = chart.timespan
if chart.timespan == 'Select Date Range':
if chart.timespan == "Select Date Range":
from_date = chart.from_date
to_date = chart.to_date
@@ -31,17 +40,23 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d
company = filters.get("company")
if not account and chart_name:
frappe.throw(_("Account is not set for the dashboard chart {0}")
.format(get_link_to_form("Dashboard Chart", chart_name)))
frappe.throw(
_("Account is not set for the dashboard chart {0}").format(
get_link_to_form("Dashboard Chart", chart_name)
)
)
if not frappe.db.exists("Account", account) and chart_name:
frappe.throw(_("Account {0} does not exists in the dashboard chart {1}")
.format(account, get_link_to_form("Dashboard Chart", chart_name)))
frappe.throw(
_("Account {0} does not exists in the dashboard chart {1}").format(
account, get_link_to_form("Dashboard Chart", chart_name)
)
)
if not to_date:
to_date = nowdate()
if not from_date:
if timegrain in ('Monthly', 'Quarterly'):
if timegrain in ("Monthly", "Quarterly"):
from_date = get_from_date_from_timespan(to_date, timespan)
# fetch dates to plot
@@ -54,16 +69,14 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d
result = build_result(account, dates, gl_entries)
return {
"labels": [formatdate(r[0].strftime('%Y-%m-%d')) for r in result],
"datasets": [{
"name": account,
"values": [r[1] for r in result]
}]
"labels": [formatdate(r[0].strftime("%Y-%m-%d")) for r in result],
"datasets": [{"name": account, "values": [r[1] for r in result]}],
}
def build_result(account, dates, gl_entries):
result = [[getdate(date), 0.0] for date in dates]
root_type = frappe.db.get_value('Account', account, 'root_type')
root_type = frappe.db.get_value("Account", account, "root_type")
# start with the first date
date_index = 0
@@ -78,30 +91,34 @@ def build_result(account, dates, gl_entries):
result[date_index][1] += entry.debit - entry.credit
# if account type is credit, switch balances
if root_type not in ('Asset', 'Expense'):
if root_type not in ("Asset", "Expense"):
for r in result:
r[1] = -1 * r[1]
# for balance sheet accounts, the totals are cumulative
if root_type in ('Asset', 'Liability', 'Equity'):
if root_type in ("Asset", "Liability", "Equity"):
for i, r in enumerate(result):
if i > 0:
r[1] = r[1] + result[i-1][1]
r[1] = r[1] + result[i - 1][1]
return result
def get_gl_entries(account, to_date):
child_accounts = get_descendants_of('Account', account, ignore_permissions=True)
child_accounts = get_descendants_of("Account", account, ignore_permissions=True)
child_accounts.append(account)
return frappe.db.get_all('GL Entry',
fields = ['posting_date', 'debit', 'credit'],
filters = [
dict(posting_date = ('<', to_date)),
dict(account = ('in', child_accounts)),
dict(voucher_type = ('!=', 'Period Closing Voucher'))
return frappe.db.get_all(
"GL Entry",
fields=["posting_date", "debit", "credit"],
filters=[
dict(posting_date=("<", to_date)),
dict(account=("in", child_accounts)),
dict(voucher_type=("!=", "Period Closing Voucher")),
],
order_by = 'posting_date asc')
order_by="posting_date asc",
)
def get_dates_from_timegrain(from_date, to_date, timegrain):
days = months = years = 0
@@ -116,6 +133,8 @@ def get_dates_from_timegrain(from_date, to_date, timegrain):
dates = [get_period_ending(from_date, timegrain)]
while getdate(dates[-1]) < getdate(to_date):
date = get_period_ending(add_to_date(dates[-1], years=years, months=months, days=days), timegrain)
date = get_period_ending(
add_to_date(dates[-1], years=years, months=months, days=days), timegrain
)
dates.append(date)
return dates

View File

@@ -1,4 +1,3 @@
import frappe
from frappe import _
from frappe.email import sendmail_to_system_managers
@@ -23,20 +22,23 @@ from erpnext.accounts.utils import get_account_currency
def validate_service_stop_date(doc):
''' Validates service_stop_date for Purchase Invoice and Sales Invoice '''
"""Validates service_stop_date for Purchase Invoice and Sales Invoice"""
enable_check = "enable_deferred_revenue" \
if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
enable_check = (
"enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
)
old_stop_dates = {}
old_doc = frappe.db.get_all("{0} Item".format(doc.doctype),
{"parent": doc.name}, ["name", "service_stop_date"])
old_doc = frappe.db.get_all(
"{0} Item".format(doc.doctype), {"parent": doc.name}, ["name", "service_stop_date"]
)
for d in old_doc:
old_stop_dates[d.name] = d.service_stop_date or ""
for item in doc.items:
if not item.get(enable_check): continue
if not item.get(enable_check):
continue
if item.service_stop_date:
if date_diff(item.service_stop_date, item.service_start_date) < 0:
@@ -45,21 +47,31 @@ def validate_service_stop_date(doc):
if date_diff(item.service_stop_date, item.service_end_date) > 0:
frappe.throw(_("Service Stop Date cannot be after Service End Date"))
if old_stop_dates and old_stop_dates.get(item.name) and item.service_stop_date!=old_stop_dates.get(item.name):
if (
old_stop_dates
and old_stop_dates.get(item.name)
and item.service_stop_date != old_stop_dates.get(item.name)
):
frappe.throw(_("Cannot change Service Stop Date for item in row {0}").format(item.idx))
def build_conditions(process_type, account, company):
conditions=''
deferred_account = "item.deferred_revenue_account" if process_type=="Income" else "item.deferred_expense_account"
conditions = ""
deferred_account = (
"item.deferred_revenue_account" if process_type == "Income" else "item.deferred_expense_account"
)
if account:
conditions += "AND %s='%s'"%(deferred_account, account)
conditions += "AND %s='%s'" % (deferred_account, account)
elif company:
conditions += f"AND p.company = {frappe.db.escape(company)}"
return conditions
def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_date=None, conditions=''):
def convert_deferred_expense_to_expense(
deferred_process, start_date=None, end_date=None, conditions=""
):
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
if not start_date:
@@ -68,14 +80,19 @@ def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_d
end_date = add_days(today(), -1)
# check for the purchase invoice for which GL entries has to be done
invoices = frappe.db.sql_list('''
invoices = frappe.db.sql_list(
"""
select distinct item.parent
from `tabPurchase Invoice Item` item, `tabPurchase Invoice` p
where item.service_start_date<=%s and item.service_end_date>=%s
and item.enable_deferred_expense = 1 and item.parent=p.name
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
{0}
'''.format(conditions), (end_date, start_date)) #nosec
""".format(
conditions
),
(end_date, start_date),
) # nosec
# For each invoice, book deferred expense
for invoice in invoices:
@@ -85,7 +102,10 @@ def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_d
if frappe.flags.deferred_accounting_error:
send_mail(deferred_process)
def convert_deferred_revenue_to_income(deferred_process, start_date=None, end_date=None, conditions=''):
def convert_deferred_revenue_to_income(
deferred_process, start_date=None, end_date=None, conditions=""
):
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
if not start_date:
@@ -94,14 +114,19 @@ def convert_deferred_revenue_to_income(deferred_process, start_date=None, end_da
end_date = add_days(today(), -1)
# check for the sales invoice for which GL entries has to be done
invoices = frappe.db.sql_list('''
invoices = frappe.db.sql_list(
"""
select distinct item.parent
from `tabSales Invoice Item` item, `tabSales Invoice` p
where item.service_start_date<=%s and item.service_end_date>=%s
and item.enable_deferred_revenue = 1 and item.parent=p.name
and item.docstatus = 1 and ifnull(item.amount, 0) > 0
{0}
'''.format(conditions), (end_date, start_date)) #nosec
""".format(
conditions
),
(end_date, start_date),
) # nosec
for invoice in invoices:
doc = frappe.get_doc("Sales Invoice", invoice)
@@ -110,30 +135,43 @@ def convert_deferred_revenue_to_income(deferred_process, start_date=None, end_da
if frappe.flags.deferred_accounting_error:
send_mail(deferred_process)
def get_booking_dates(doc, item, posting_date=None):
if not posting_date:
posting_date = add_days(today(), -1)
last_gl_entry = False
deferred_account = "deferred_revenue_account" if doc.doctype=="Sales Invoice" else "deferred_expense_account"
deferred_account = (
"deferred_revenue_account" if doc.doctype == "Sales Invoice" else "deferred_expense_account"
)
prev_gl_entry = frappe.db.sql('''
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)
""",
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
as_dict=True,
)
prev_gl_via_je = frappe.db.sql('''
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)
""",
(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):
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:
@@ -157,66 +195,94 @@ def get_booking_dates(doc, item, posting_date=None):
else:
return None, None, None
def calculate_monthly_amount(doc, item, last_gl_entry, start_date, end_date, total_days, total_booking_days, account_currency):
def calculate_monthly_amount(
doc, item, last_gl_entry, start_date, end_date, total_days, total_booking_days, account_currency
):
amount, base_amount = 0, 0
if not last_gl_entry:
total_months = (item.service_end_date.year - item.service_start_date.year) * 12 + \
(item.service_end_date.month - item.service_start_date.month) + 1
total_months = (
(item.service_end_date.year - item.service_start_date.year) * 12
+ (item.service_end_date.month - item.service_start_date.month)
+ 1
)
prorate_factor = flt(date_diff(item.service_end_date, item.service_start_date)) \
/ flt(date_diff(get_last_day(item.service_end_date), get_first_day(item.service_start_date)))
prorate_factor = flt(date_diff(item.service_end_date, item.service_start_date)) / flt(
date_diff(get_last_day(item.service_end_date), get_first_day(item.service_start_date))
)
actual_months = rounded(total_months * prorate_factor, 1)
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
doc, item
)
base_amount = flt(item.base_net_amount / actual_months, item.precision("base_net_amount"))
if base_amount + already_booked_amount > item.base_net_amount:
base_amount = item.base_net_amount - already_booked_amount
if account_currency==doc.company_currency:
if account_currency == doc.company_currency:
amount = base_amount
else:
amount = flt(item.net_amount/actual_months, item.precision("net_amount"))
amount = flt(item.net_amount / actual_months, item.precision("net_amount"))
if amount + already_booked_amount_in_account_currency > item.net_amount:
amount = item.net_amount - already_booked_amount_in_account_currency
if not (get_first_day(start_date) == start_date and get_last_day(end_date) == end_date):
partial_month = flt(date_diff(end_date, start_date)) \
/ flt(date_diff(get_last_day(end_date), get_first_day(start_date)))
partial_month = flt(date_diff(end_date, start_date)) / flt(
date_diff(get_last_day(end_date), get_first_day(start_date))
)
base_amount = rounded(partial_month, 1) * base_amount
amount = rounded(partial_month, 1) * amount
else:
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
if account_currency==doc.company_currency:
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
doc, item
)
base_amount = flt(
item.base_net_amount - already_booked_amount, item.precision("base_net_amount")
)
if account_currency == doc.company_currency:
amount = base_amount
else:
amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
amount = flt(
item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount")
)
return amount, base_amount
def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, account_currency):
amount, base_amount = 0, 0
if not last_gl_entry:
base_amount = flt(item.base_net_amount*total_booking_days/flt(total_days), item.precision("base_net_amount"))
if account_currency==doc.company_currency:
base_amount = flt(
item.base_net_amount * total_booking_days / flt(total_days), item.precision("base_net_amount")
)
if account_currency == doc.company_currency:
amount = base_amount
else:
amount = flt(item.net_amount*total_booking_days/flt(total_days), item.precision("net_amount"))
amount = flt(
item.net_amount * total_booking_days / flt(total_days), item.precision("net_amount")
)
else:
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(doc, item)
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
doc, item
)
base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
if account_currency==doc.company_currency:
base_amount = flt(
item.base_net_amount - already_booked_amount, item.precision("base_net_amount")
)
if account_currency == doc.company_currency:
amount = base_amount
else:
amount = flt(item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount"))
amount = flt(
item.net_amount - already_booked_amount_in_account_currency, item.precision("net_amount")
)
return amount, base_amount
def get_already_booked_amount(doc, item):
if doc.doctype == "Sales Invoice":
total_credit_debit, total_credit_debit_currency = "debit", "debit_in_account_currency"
@@ -225,20 +291,31 @@ def get_already_booked_amount(doc, item):
total_credit_debit, total_credit_debit_currency = "credit", "credit_in_account_currency"
deferred_account = "deferred_expense_account"
gl_entries_details = frappe.db.sql('''
gl_entries_details = frappe.db.sql(
"""
select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no
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
group by voucher_detail_no
'''.format(total_credit_debit, total_credit_debit_currency),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
""".format(
total_credit_debit, total_credit_debit_currency
),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
as_dict=True,
)
journal_entry_details = frappe.db.sql('''
journal_entry_details = frappe.db.sql(
"""
SELECT sum(c.{0}) as total_credit, sum(c.{1}) as total_credit_in_account_currency, reference_detail_no
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 p.docstatus < 2 group by reference_detail_no
'''.format(total_credit_debit, total_credit_debit_currency),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), as_dict=True)
""".format(
total_credit_debit, total_credit_debit_currency
),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
as_dict=True,
)
already_booked_amount = gl_entries_details[0].total_credit if gl_entries_details else 0
already_booked_amount += journal_entry_details[0].total_credit if journal_entry_details else 0
@@ -246,20 +323,29 @@ def get_already_booked_amount(doc, item):
if doc.currency == doc.company_currency:
already_booked_amount_in_account_currency = already_booked_amount
else:
already_booked_amount_in_account_currency = gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0
already_booked_amount_in_account_currency += journal_entry_details[0].total_credit_in_account_currency if journal_entry_details else 0
already_booked_amount_in_account_currency = (
gl_entries_details[0].total_credit_in_account_currency if gl_entries_details else 0
)
already_booked_amount_in_account_currency += (
journal_entry_details[0].total_credit_in_account_currency if journal_entry_details else 0
)
return already_booked_amount, already_booked_amount_in_account_currency
def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
enable_check = "enable_deferred_revenue" \
if doc.doctype=="Sales Invoice" else "enable_deferred_expense"
enable_check = (
"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.get_cached_value("Accounts Settings", "None", "acc_frozen_upto")
def _book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on):
def _book_deferred_revenue_or_expense(
item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on
):
start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date)
if not (start_date and end_date): return
if not (start_date and end_date):
return
account_currency = get_account_currency(item.expense_account or item.income_account)
if doc.doctype == "Sales Invoice":
@@ -272,107 +358,179 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
total_days = date_diff(item.service_end_date, item.service_start_date) + 1
total_booking_days = date_diff(end_date, start_date) + 1
if book_deferred_entries_based_on == 'Months':
amount, base_amount = calculate_monthly_amount(doc, item, last_gl_entry,
start_date, end_date, total_days, total_booking_days, account_currency)
if book_deferred_entries_based_on == "Months":
amount, base_amount = calculate_monthly_amount(
doc,
item,
last_gl_entry,
start_date,
end_date,
total_days,
total_booking_days,
account_currency,
)
else:
amount, base_amount = calculate_amount(doc, item, last_gl_entry,
total_days, total_booking_days, account_currency)
amount, base_amount = calculate_amount(
doc, item, last_gl_entry, total_days, total_booking_days, account_currency
)
if not amount:
return
# check if books nor frozen till endate:
if getdate(end_date) >= getdate(accounts_frozen_upto):
if accounts_frozen_upto and (end_date) <= getdate(accounts_frozen_upto):
end_date = get_last_day(add_days(accounts_frozen_upto, 1))
if via_journal_entry:
book_revenue_via_journal_entry(doc, credit_account, debit_account, against, amount,
base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process, submit_journal_entry)
book_revenue_via_journal_entry(
doc,
credit_account,
debit_account,
amount,
base_amount,
end_date,
project,
account_currency,
item.cost_center,
item,
deferred_process,
submit_journal_entry,
)
else:
make_gl_entries(doc, credit_account, debit_account, against,
amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process)
make_gl_entries(
doc,
credit_account,
debit_account,
against,
amount,
base_amount,
end_date,
project,
account_currency,
item.cost_center,
item,
deferred_process,
)
# Returned in case of any errors because it tries to submit the same record again and again in case of errors
if frappe.flags.deferred_accounting_error:
return
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)
_book_deferred_revenue_or_expense(
item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on
)
via_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_via_journal_entry'))
submit_journal_entry = cint(frappe.db.get_singles_value('Accounts Settings', 'submit_journal_entries'))
book_deferred_entries_based_on = frappe.db.get_singles_value('Accounts Settings', 'book_deferred_entries_based_on')
via_journal_entry = cint(
frappe.db.get_singles_value("Accounts Settings", "book_deferred_entries_via_journal_entry")
)
submit_journal_entry = cint(
frappe.db.get_singles_value("Accounts Settings", "submit_journal_entries")
)
book_deferred_entries_based_on = frappe.db.get_singles_value(
"Accounts Settings", "book_deferred_entries_based_on"
)
for item in doc.get('items'):
for item in doc.get("items"):
if item.get(enable_check):
_book_deferred_revenue_or_expense(item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on)
_book_deferred_revenue_or_expense(
item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on
)
def process_deferred_accounting(posting_date=None):
''' Converts deferred income/expense into income/expense
Executed via background jobs on every month end '''
"""Converts deferred income/expense into income/expense
Executed via background jobs on every month end"""
if not posting_date:
posting_date = today()
if not cint(frappe.db.get_singles_value('Accounts Settings', 'automatically_process_deferred_accounting_entry')):
if not cint(
frappe.db.get_singles_value(
"Accounts Settings", "automatically_process_deferred_accounting_entry"
)
):
return
start_date = add_months(today(), -1)
end_date = add_days(today(), -1)
companies = frappe.get_all('Company')
companies = frappe.get_all("Company")
for company in companies:
for record_type in ('Income', 'Expense'):
doc = frappe.get_doc(dict(
doctype='Process Deferred Accounting',
company=company.name,
posting_date=posting_date,
start_date=start_date,
end_date=end_date,
type=record_type
))
for record_type in ("Income", "Expense"):
doc = frappe.get_doc(
dict(
doctype="Process Deferred Accounting",
company=company.name,
posting_date=posting_date,
start_date=start_date,
end_date=end_date,
type=record_type,
)
)
doc.insert()
doc.submit()
def make_gl_entries(doc, credit_account, debit_account, against,
amount, base_amount, posting_date, project, account_currency, cost_center, item, deferred_process=None):
def make_gl_entries(
doc,
credit_account,
debit_account,
against,
amount,
base_amount,
posting_date,
project,
account_currency,
cost_center,
item,
deferred_process=None,
):
# GL Entry for crediting the amount in the deferred expense
from erpnext.accounts.general_ledger import make_gl_entries
if amount == 0: return
if amount == 0:
return
gl_entries = []
gl_entries.append(
doc.get_gl_dict({
"account": credit_account,
"against": against,
"credit": base_amount,
"credit_in_account_currency": amount,
"cost_center": cost_center,
"voucher_detail_no": item.name,
'posting_date': posting_date,
'project': project,
'against_voucher_type': 'Process Deferred Accounting',
'against_voucher': deferred_process
}, account_currency, item=item)
doc.get_gl_dict(
{
"account": credit_account,
"against": against,
"credit": base_amount,
"credit_in_account_currency": amount,
"cost_center": cost_center,
"voucher_detail_no": item.name,
"posting_date": posting_date,
"project": project,
"against_voucher_type": "Process Deferred Accounting",
"against_voucher": deferred_process,
},
account_currency,
item=item,
)
)
# GL Entry to debit the amount from the expense
gl_entries.append(
doc.get_gl_dict({
"account": debit_account,
"against": against,
"debit": base_amount,
"debit_in_account_currency": amount,
"cost_center": cost_center,
"voucher_detail_no": item.name,
'posting_date': posting_date,
'project': project,
'against_voucher_type': 'Process Deferred Accounting',
'against_voucher': deferred_process
}, account_currency, item=item)
doc.get_gl_dict(
{
"account": debit_account,
"against": against,
"debit": base_amount,
"debit_in_account_currency": amount,
"cost_center": cost_center,
"voucher_detail_no": item.name,
"posting_date": posting_date,
"project": project,
"against_voucher_type": "Process Deferred Accounting",
"against_voucher": deferred_process,
},
account_currency,
item=item,
)
)
if gl_entries:
@@ -382,68 +540,88 @@ def make_gl_entries(doc, credit_account, debit_account, against,
except Exception as e:
if frappe.flags.in_test:
traceback = frappe.get_traceback()
frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
frappe.log_error(
title=_("Error while processing deferred accounting for Invoice {0}").format(doc.name),
message=traceback,
)
raise e
else:
frappe.db.rollback()
traceback = frappe.get_traceback()
frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
frappe.log_error(
title=_("Error while processing deferred accounting for Invoice {0}").format(doc.name),
message=traceback,
)
frappe.flags.deferred_accounting_error = True
def send_mail(deferred_process):
title = _("Error while processing deferred accounting for {0}").format(deferred_process)
link = get_link_to_form('Process Deferred Accounting', deferred_process)
link = get_link_to_form("Process Deferred Accounting", deferred_process)
content = _("Deferred accounting failed for some invoices:") + "\n"
content += _("Please check Process Deferred Accounting {0} and submit manually after resolving errors.").format(link)
content += _(
"Please check Process Deferred Accounting {0} and submit manually after resolving errors."
).format(link)
sendmail_to_system_managers(title, content)
def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
amount, base_amount, posting_date, project, account_currency, cost_center, item,
deferred_process=None, submit='No'):
if amount == 0: return
def book_revenue_via_journal_entry(
doc,
credit_account,
debit_account,
amount,
base_amount,
posting_date,
project,
account_currency,
cost_center,
item,
deferred_process=None,
submit="No",
):
journal_entry = frappe.new_doc('Journal Entry')
if amount == 0:
return
journal_entry = frappe.new_doc("Journal Entry")
journal_entry.posting_date = posting_date
journal_entry.company = doc.company
journal_entry.voucher_type = 'Deferred Revenue' if doc.doctype == 'Sales Invoice' \
else 'Deferred Expense'
journal_entry.voucher_type = (
"Deferred Revenue" if doc.doctype == "Sales Invoice" else "Deferred Expense"
)
journal_entry.process_deferred_accounting = deferred_process
debit_entry = {
'account': credit_account,
'credit': base_amount,
'credit_in_account_currency': amount,
'account_currency': account_currency,
'reference_name': doc.name,
'reference_type': doc.doctype,
'reference_detail_no': item.name,
'cost_center': cost_center,
'project': project,
"account": credit_account,
"credit": base_amount,
"credit_in_account_currency": amount,
"account_currency": account_currency,
"reference_name": doc.name,
"reference_type": doc.doctype,
"reference_detail_no": item.name,
"cost_center": cost_center,
"project": project,
}
credit_entry = {
'account': debit_account,
'debit': base_amount,
'debit_in_account_currency': amount,
'account_currency': account_currency,
'reference_name': doc.name,
'reference_type': doc.doctype,
'reference_detail_no': item.name,
'cost_center': cost_center,
'project': project,
"account": debit_account,
"debit": base_amount,
"debit_in_account_currency": amount,
"account_currency": account_currency,
"reference_name": doc.name,
"reference_type": doc.doctype,
"reference_detail_no": item.name,
"cost_center": cost_center,
"project": project,
}
for dimension in get_accounting_dimensions():
debit_entry.update({
dimension: item.get(dimension)
})
debit_entry.update({dimension: item.get(dimension)})
credit_entry.update({
dimension: item.get(dimension)
})
credit_entry.update({dimension: item.get(dimension)})
journal_entry.append('accounts', debit_entry)
journal_entry.append('accounts', credit_entry)
journal_entry.append("accounts", debit_entry)
journal_entry.append("accounts", credit_entry)
try:
journal_entry.save()
@@ -455,20 +633,30 @@ def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
except Exception:
frappe.db.rollback()
traceback = frappe.get_traceback()
frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
frappe.log_error(
title=_("Error while processing deferred accounting for Invoice {0}").format(doc.name),
message=traceback,
)
frappe.flags.deferred_accounting_error = True
def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
if doctype == 'Sales Invoice':
credit_account, debit_account = frappe.db.get_value('Sales Invoice Item', {'name': voucher_detail_no},
['income_account', 'deferred_revenue_account'])
if doctype == "Sales Invoice":
credit_account, debit_account = frappe.db.get_value(
"Sales Invoice Item",
{"name": voucher_detail_no},
["income_account", "deferred_revenue_account"],
)
else:
credit_account, debit_account = frappe.db.get_value('Purchase Invoice Item', {'name': voucher_detail_no},
['deferred_expense_account', 'expense_account'])
credit_account, debit_account = frappe.db.get_value(
"Purchase Invoice Item",
{"name": voucher_detail_no},
["deferred_expense_account", "expense_account"],
)
if dr_or_cr == 'Debit':
if dr_or_cr == "Debit":
return debit_account
else:
return credit_account

View File

@@ -10,11 +10,17 @@ from frappe.utils.nestedset import NestedSet, get_ancestors_of, get_descendants_
import erpnext
class RootNotEditable(frappe.ValidationError): pass
class BalanceMismatchError(frappe.ValidationError): pass
class RootNotEditable(frappe.ValidationError):
pass
class BalanceMismatchError(frappe.ValidationError):
pass
class Account(NestedSet):
nsm_parent_field = 'parent_account'
nsm_parent_field = "parent_account"
def on_update(self):
if frappe.local.flags.ignore_update_nsm:
return
@@ -22,17 +28,20 @@ class Account(NestedSet):
super(Account, self).on_update()
def onload(self):
frozen_accounts_modifier = frappe.db.get_value("Accounts Settings", "Accounts Settings",
"frozen_accounts_modifier")
frozen_accounts_modifier = frappe.db.get_value(
"Accounts Settings", "Accounts Settings", "frozen_accounts_modifier"
)
if not frozen_accounts_modifier or frozen_accounts_modifier in frappe.get_roles():
self.set_onload("can_freeze_account", True)
def autoname(self):
from erpnext.accounts.utils import get_autoname_with_number
self.name = get_autoname_with_number(self.account_number, self.account_name, None, self.company)
def validate(self):
from erpnext.accounts.utils import validate_field_number
if frappe.local.flags.allow_unverified_charts:
return
self.validate_parent()
@@ -49,22 +58,33 @@ class Account(NestedSet):
def validate_parent(self):
"""Fetch Parent Details and validate parent account"""
if self.parent_account:
par = frappe.db.get_value("Account", self.parent_account,
["name", "is_group", "company"], as_dict=1)
par = frappe.db.get_value(
"Account", self.parent_account, ["name", "is_group", "company"], as_dict=1
)
if not par:
throw(_("Account {0}: Parent account {1} does not exist").format(self.name, self.parent_account))
throw(
_("Account {0}: Parent account {1} does not exist").format(self.name, self.parent_account)
)
elif par.name == self.name:
throw(_("Account {0}: You can not assign itself as parent account").format(self.name))
elif not par.is_group:
throw(_("Account {0}: Parent account {1} can not be a ledger").format(self.name, self.parent_account))
throw(
_("Account {0}: Parent account {1} can not be a ledger").format(
self.name, self.parent_account
)
)
elif par.company != self.company:
throw(_("Account {0}: Parent account {1} does not belong to company: {2}")
.format(self.name, self.parent_account, self.company))
throw(
_("Account {0}: Parent account {1} does not belong to company: {2}").format(
self.name, self.parent_account, self.company
)
)
def set_root_and_report_type(self):
if self.parent_account:
par = frappe.db.get_value("Account", self.parent_account,
["report_type", "root_type"], as_dict=1)
par = frappe.db.get_value(
"Account", self.parent_account, ["report_type", "root_type"], as_dict=1
)
if par.report_type:
self.report_type = par.report_type
@@ -75,15 +95,20 @@ class Account(NestedSet):
db_value = frappe.db.get_value("Account", self.name, ["report_type", "root_type"], as_dict=1)
if db_value:
if self.report_type != db_value.report_type:
frappe.db.sql("update `tabAccount` set report_type=%s where lft > %s and rgt < %s",
(self.report_type, self.lft, self.rgt))
frappe.db.sql(
"update `tabAccount` set report_type=%s where lft > %s and rgt < %s",
(self.report_type, self.lft, self.rgt),
)
if self.root_type != db_value.root_type:
frappe.db.sql("update `tabAccount` set root_type=%s where lft > %s and rgt < %s",
(self.root_type, self.lft, self.rgt))
frappe.db.sql(
"update `tabAccount` set root_type=%s where lft > %s and rgt < %s",
(self.root_type, self.lft, self.rgt),
)
if self.root_type and not self.report_type:
self.report_type = "Balance Sheet" \
if self.root_type in ("Asset", "Liability", "Equity") else "Profit and Loss"
self.report_type = (
"Balance Sheet" if self.root_type in ("Asset", "Liability", "Equity") else "Profit and Loss"
)
def validate_root_details(self):
# does not exists parent
@@ -96,21 +121,26 @@ class Account(NestedSet):
def validate_root_company_and_sync_account_to_children(self):
# ignore validation while creating new compnay or while syncing to child companies
if frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation:
if (
frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation
):
return
ancestors = get_root_company(self.company)
if ancestors:
if frappe.get_value("Company", self.company, "allow_account_creation_against_child_company"):
return
if not frappe.db.get_value("Account",
{'account_name': self.account_name, 'company': ancestors[0]}, 'name'):
if not frappe.db.get_value(
"Account", {"account_name": self.account_name, "company": ancestors[0]}, "name"
):
frappe.throw(_("Please add the account to root level Company - {}").format(ancestors[0]))
elif self.parent_account:
descendants = get_descendants_of('Company', self.company)
if not descendants: return
descendants = get_descendants_of("Company", self.company)
if not descendants:
return
parent_acc_name_map = {}
parent_acc_name, parent_acc_number = frappe.db.get_value('Account', self.parent_account, \
["account_name", "account_number"])
parent_acc_name, parent_acc_number = frappe.db.get_value(
"Account", self.parent_account, ["account_name", "account_number"]
)
filters = {
"company": ["in", descendants],
"account_name": parent_acc_name,
@@ -118,10 +148,13 @@ class Account(NestedSet):
if parent_acc_number:
filters["account_number"] = parent_acc_number
for d in frappe.db.get_values('Account', filters=filters, fieldname=["company", "name"], as_dict=True):
for d in frappe.db.get_values(
"Account", filters=filters, fieldname=["company", "name"], as_dict=True
):
parent_acc_name_map[d["company"]] = d["name"]
if not parent_acc_name_map: return
if not parent_acc_name_map:
return
self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
@@ -142,26 +175,38 @@ class Account(NestedSet):
def validate_frozen_accounts_modifier(self):
old_value = frappe.db.get_value("Account", self.name, "freeze_account")
if old_value and old_value != self.freeze_account:
frozen_accounts_modifier = frappe.db.get_value('Accounts Settings', None, 'frozen_accounts_modifier')
if not frozen_accounts_modifier or \
frozen_accounts_modifier not in frappe.get_roles():
throw(_("You are not authorized to set Frozen value"))
frozen_accounts_modifier = frappe.db.get_value(
"Accounts Settings", None, "frozen_accounts_modifier"
)
if not frozen_accounts_modifier or frozen_accounts_modifier not in frappe.get_roles():
throw(_("You are not authorized to set Frozen value"))
def validate_balance_must_be_debit_or_credit(self):
from erpnext.accounts.utils import get_balance_on
if not self.get("__islocal") and self.balance_must_be:
account_balance = get_balance_on(self.name)
if account_balance > 0 and self.balance_must_be == "Credit":
frappe.throw(_("Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'"))
frappe.throw(
_(
"Account balance already in Debit, you are not allowed to set 'Balance Must Be' as 'Credit'"
)
)
elif account_balance < 0 and self.balance_must_be == "Debit":
frappe.throw(_("Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'"))
frappe.throw(
_(
"Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'"
)
)
def validate_account_currency(self):
if not self.account_currency:
self.account_currency = frappe.get_cached_value('Company', self.company, "default_currency")
self.account_currency = frappe.get_cached_value("Company", self.company, "default_currency")
elif self.account_currency != frappe.db.get_value("Account", self.name, "account_currency"):
gl_currency = frappe.db.get_value("GL Entry", {"account": self.name}, "account_currency")
if gl_currency and self.account_currency != gl_currency:
if frappe.db.get_value("GL Entry", {"account": self.name}):
frappe.throw(_("Currency can not be changed after making entries using some other currency"))
@@ -170,45 +215,52 @@ class Account(NestedSet):
company_bold = frappe.bold(company)
parent_acc_name_bold = frappe.bold(parent_acc_name)
if not parent_acc_name_map.get(company):
frappe.throw(_("While creating account for Child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA")
.format(company_bold, parent_acc_name_bold), title=_("Account Not Found"))
frappe.throw(
_(
"While creating account for Child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA"
).format(company_bold, parent_acc_name_bold),
title=_("Account Not Found"),
)
# validate if parent of child company account to be added is a group
if (frappe.db.get_value("Account", self.parent_account, "is_group")
and not frappe.db.get_value("Account", parent_acc_name_map[company], "is_group")):
msg = _("While creating account for Child Company {0}, parent account {1} found as a ledger account.").format(company_bold, parent_acc_name_bold)
if frappe.db.get_value("Account", self.parent_account, "is_group") and not frappe.db.get_value(
"Account", parent_acc_name_map[company], "is_group"
):
msg = _(
"While creating account for Child Company {0}, parent account {1} found as a ledger account."
).format(company_bold, parent_acc_name_bold)
msg += "<br><br>"
msg += _("Please convert the parent account in corresponding child company to a group account.")
msg += _(
"Please convert the parent account in corresponding child company to a group account."
)
frappe.throw(msg, title=_("Invalid Parent Account"))
filters = {
"account_name": self.account_name,
"company": company
}
filters = {"account_name": self.account_name, "company": company}
if self.account_number:
filters["account_number"] = self.account_number
child_account = frappe.db.get_value("Account", filters, 'name')
child_account = frappe.db.get_value("Account", filters, "name")
if not child_account:
doc = frappe.copy_doc(self)
doc.flags.ignore_root_company_validation = True
doc.update({
"company": company,
# parent account's currency should be passed down to child account's curreny
# if it is None, it picks it up from default company currency, which might be unintended
"account_currency": erpnext.get_company_currency(company),
"parent_account": parent_acc_name_map[company]
})
doc.update(
{
"company": company,
# parent account's currency should be passed down to child account's curreny
# if it is None, it picks it up from default company currency, which might be unintended
"account_currency": erpnext.get_company_currency(company),
"parent_account": parent_acc_name_map[company],
}
)
doc.save()
frappe.msgprint(_("Account {0} is added in the child company {1}")
.format(doc.name, company))
frappe.msgprint(_("Account {0} is added in the child company {1}").format(doc.name, company))
elif child_account:
# update the parent company's value in child companies
doc = frappe.get_doc("Account", child_account)
parent_value_changed = False
for field in ['account_type', 'freeze_account', 'balance_must_be']:
for field in ["account_type", "freeze_account", "balance_must_be"]:
if doc.get(field) != self.get(field):
parent_value_changed = True
doc.set(field, self.get(field))
@@ -243,8 +295,11 @@ class Account(NestedSet):
return frappe.db.get_value("GL Entry", {"account": self.name})
def check_if_child_exists(self):
return frappe.db.sql("""select name from `tabAccount` where parent_account = %s
and docstatus != 2""", self.name)
return frappe.db.sql(
"""select name from `tabAccount` where parent_account = %s
and docstatus != 2""",
self.name,
)
def validate_mandatory(self):
if not self.root_type:
@@ -260,73 +315,99 @@ class Account(NestedSet):
super(Account, self).on_trash(True)
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select name from tabAccount
return frappe.db.sql(
"""select name from tabAccount
where is_group = 1 and docstatus != 2 and company = %s
and %s like %s order by name limit %s, %s""" %
("%s", searchfield, "%s", "%s", "%s"),
(filters["company"], "%%%s%%" % txt, start, page_len), as_list=1)
and %s like %s order by name limit %s, %s"""
% ("%s", searchfield, "%s", "%s", "%s"),
(filters["company"], "%%%s%%" % txt, start, page_len),
as_list=1,
)
def get_account_currency(account):
"""Helper function to get account currency"""
if not account:
return
def generator():
account_currency, company = frappe.get_cached_value("Account", account, ["account_currency", "company"])
account_currency, company = frappe.get_cached_value(
"Account", account, ["account_currency", "company"]
)
if not account_currency:
account_currency = frappe.get_cached_value('Company', company, "default_currency")
account_currency = frappe.get_cached_value("Company", company, "default_currency")
return account_currency
return frappe.local_cache("account_currency", account, generator)
def on_doctype_update():
frappe.db.add_index("Account", ["lft", "rgt"])
def get_account_autoname(account_number, account_name, company):
# first validate if company exists
company = frappe.get_cached_value('Company', company, ["abbr", "name"], as_dict=True)
company = frappe.get_cached_value("Company", company, ["abbr", "name"], as_dict=True)
if not company:
frappe.throw(_('Company {0} does not exist').format(company))
frappe.throw(_("Company {0} does not exist").format(company))
parts = [account_name.strip(), company.abbr]
if cstr(account_number).strip():
parts.insert(0, cstr(account_number).strip())
return ' - '.join(parts)
return " - ".join(parts)
def validate_account_number(name, account_number, company):
if account_number:
account_with_same_number = frappe.db.get_value("Account",
{"account_number": account_number, "company": company, "name": ["!=", name]})
account_with_same_number = frappe.db.get_value(
"Account", {"account_number": account_number, "company": company, "name": ["!=", name]}
)
if account_with_same_number:
frappe.throw(_("Account Number {0} already used in account {1}")
.format(account_number, account_with_same_number))
frappe.throw(
_("Account Number {0} already used in account {1}").format(
account_number, account_with_same_number
)
)
@frappe.whitelist()
def update_account_number(name, account_name, account_number=None, from_descendant=False):
account = frappe.db.get_value("Account", name, "company", as_dict=True)
if not account: return
if not account:
return
old_acc_name, old_acc_number = frappe.db.get_value('Account', name, \
["account_name", "account_number"])
old_acc_name, old_acc_number = frappe.db.get_value(
"Account", name, ["account_name", "account_number"]
)
# check if account exists in parent company
ancestors = get_ancestors_of("Company", account.company)
allow_independent_account_creation = frappe.get_value("Company", account.company, "allow_account_creation_against_child_company")
allow_independent_account_creation = frappe.get_value(
"Company", account.company, "allow_account_creation_against_child_company"
)
if ancestors and not allow_independent_account_creation:
for ancestor in ancestors:
if frappe.db.get_value("Account", {'account_name': old_acc_name, 'company': ancestor}, 'name'):
if frappe.db.get_value("Account", {"account_name": old_acc_name, "company": ancestor}, "name"):
# same account in parent company exists
allow_child_account_creation = _("Allow Account Creation Against Child Company")
message = _("Account {0} exists in parent company {1}.").format(frappe.bold(old_acc_name), frappe.bold(ancestor))
message = _("Account {0} exists in parent company {1}.").format(
frappe.bold(old_acc_name), frappe.bold(ancestor)
)
message += "<br>"
message += _("Renaming it is only allowed via parent company {0}, to avoid mismatch.").format(frappe.bold(ancestor))
message += _("Renaming it is only allowed via parent company {0}, to avoid mismatch.").format(
frappe.bold(ancestor)
)
message += "<br><br>"
message += _("To overrule this, enable '{0}' in company {1}").format(allow_child_account_creation, frappe.bold(account.company))
message += _("To overrule this, enable '{0}' in company {1}").format(
allow_child_account_creation, frappe.bold(account.company)
)
frappe.throw(message, title=_("Rename Not Allowed"))
@@ -339,42 +420,53 @@ def update_account_number(name, account_name, account_number=None, from_descenda
if not from_descendant:
# Update and rename in child company accounts as well
descendants = get_descendants_of('Company', account.company)
descendants = get_descendants_of("Company", account.company)
if descendants:
sync_update_account_number_in_child(descendants, old_acc_name, account_name, account_number, old_acc_number)
sync_update_account_number_in_child(
descendants, old_acc_name, account_name, account_number, old_acc_number
)
new_name = get_account_autoname(account_number, account_name, account.company)
if name != new_name:
frappe.rename_doc("Account", name, new_name, force=1)
return new_name
@frappe.whitelist()
def merge_account(old, new, is_group, root_type, company):
# Validate properties before merging
if not frappe.db.exists("Account", new):
throw(_("Account {0} does not exist").format(new))
val = list(frappe.db.get_value("Account", new,
["is_group", "root_type", "company"]))
val = list(frappe.db.get_value("Account", new, ["is_group", "root_type", "company"]))
if val != [cint(is_group), root_type, company]:
throw(_("""Merging is only possible if following properties are same in both records. Is Group, Root Type, Company"""))
throw(
_(
"""Merging is only possible if following properties are same in both records. Is Group, Root Type, Company"""
)
)
if is_group and frappe.db.get_value("Account", new, "parent_account") == old:
frappe.db.set_value("Account", new, "parent_account",
frappe.db.get_value("Account", old, "parent_account"))
frappe.db.set_value(
"Account", new, "parent_account", frappe.db.get_value("Account", old, "parent_account")
)
frappe.rename_doc("Account", old, new, merge=1, force=1)
return new
@frappe.whitelist()
def get_root_company(company):
# return the topmost company in the hierarchy
ancestors = get_ancestors_of('Company', company, "lft asc")
ancestors = get_ancestors_of("Company", company, "lft asc")
return [ancestors[0]] if ancestors else []
def sync_update_account_number_in_child(descendants, old_acc_name, account_name, account_number=None, old_acc_number=None):
def sync_update_account_number_in_child(
descendants, old_acc_name, account_name, account_number=None, old_acc_number=None
):
filters = {
"company": ["in", descendants],
"account_name": old_acc_name,
@@ -382,5 +474,7 @@ def sync_update_account_number_in_child(descendants, old_acc_name, account_name,
if old_acc_number:
filters["account_number"] = old_acc_number
for d in frappe.db.get_values('Account', filters=filters, fieldname=["company", "name"], as_dict=True):
update_account_number(d["name"], account_name, account_number, from_descendant=True)
for d in frappe.db.get_values(
"Account", filters=filters, fieldname=["company", "name"], as_dict=True
):
update_account_number(d["name"], account_name, account_number, from_descendant=True)

View File

@@ -11,7 +11,9 @@ from six import iteritems
from unidecode import unidecode
def create_charts(company, chart_template=None, existing_company=None, custom_chart=None, from_coa_importer=None):
def create_charts(
company, chart_template=None, existing_company=None, custom_chart=None, from_coa_importer=None
):
chart = custom_chart or get_chart(chart_template, existing_company)
if chart:
accounts = []
@@ -21,30 +23,41 @@ def create_charts(company, chart_template=None, existing_company=None, custom_ch
if root_account:
root_type = child.get("root_type")
if account_name not in ["account_name", "account_number", "account_type",
"root_type", "is_group", "tax_rate"]:
if account_name not in [
"account_name",
"account_number",
"account_type",
"root_type",
"is_group",
"tax_rate",
]:
account_number = cstr(child.get("account_number")).strip()
account_name, account_name_in_db = add_suffix_if_duplicate(account_name,
account_number, accounts)
account_name, account_name_in_db = add_suffix_if_duplicate(
account_name, account_number, accounts
)
is_group = identify_is_group(child)
report_type = "Balance Sheet" if root_type in ["Asset", "Liability", "Equity"] \
else "Profit and Loss"
report_type = (
"Balance Sheet" if root_type in ["Asset", "Liability", "Equity"] else "Profit and Loss"
)
account = frappe.get_doc({
"doctype": "Account",
"account_name": child.get('account_name') if from_coa_importer else account_name,
"company": company,
"parent_account": parent,
"is_group": is_group,
"root_type": root_type,
"report_type": report_type,
"account_number": account_number,
"account_type": child.get("account_type"),
"account_currency": child.get('account_currency') or frappe.db.get_value('Company', company, "default_currency"),
"tax_rate": child.get("tax_rate")
})
account = frappe.get_doc(
{
"doctype": "Account",
"account_name": child.get("account_name") if from_coa_importer else account_name,
"company": company,
"parent_account": parent,
"is_group": is_group,
"root_type": root_type,
"report_type": report_type,
"account_number": account_number,
"account_type": child.get("account_type"),
"account_currency": child.get("account_currency")
or frappe.db.get_value("Company", company, "default_currency"),
"tax_rate": child.get("tax_rate"),
}
)
if root_account or frappe.local.flags.allow_unverified_charts:
account.flags.ignore_mandatory = True
@@ -64,10 +77,10 @@ def create_charts(company, chart_template=None, existing_company=None, custom_ch
rebuild_tree("Account", "parent_account")
frappe.local.flags.ignore_update_nsm = False
def add_suffix_if_duplicate(account_name, account_number, accounts):
if account_number:
account_name_in_db = unidecode(" - ".join([account_number,
account_name.strip().lower()]))
account_name_in_db = unidecode(" - ".join([account_number, account_name.strip().lower()]))
else:
account_name_in_db = unidecode(account_name.strip().lower())
@@ -77,16 +90,21 @@ def add_suffix_if_duplicate(account_name, account_number, accounts):
return account_name, account_name_in_db
def identify_is_group(child):
if child.get("is_group"):
is_group = child.get("is_group")
elif len(set(child.keys()) - set(["account_name", "account_type", "root_type", "is_group", "tax_rate", "account_number"])):
elif len(
set(child.keys())
- set(["account_name", "account_type", "root_type", "is_group", "tax_rate", "account_number"])
):
is_group = 1
else:
is_group = 0
return is_group
def get_chart(chart_template, existing_company=None):
chart = {}
if existing_company:
@@ -96,11 +114,13 @@ def get_chart(chart_template, existing_company=None):
from erpnext.accounts.doctype.account.chart_of_accounts.verified import (
standard_chart_of_accounts,
)
return standard_chart_of_accounts.get()
elif chart_template == "Standard with Numbers":
from erpnext.accounts.doctype.account.chart_of_accounts.verified import (
standard_chart_of_accounts_with_account_number,
)
return standard_chart_of_accounts_with_account_number.get()
else:
folders = ("verified",)
@@ -116,6 +136,7 @@ def get_chart(chart_template, existing_company=None):
if chart and json.loads(chart).get("name") == chart_template:
return json.loads(chart).get("tree")
@frappe.whitelist()
def get_charts_for_country(country, with_standard=False):
charts = []
@@ -123,9 +144,10 @@ def get_charts_for_country(country, with_standard=False):
def _get_chart_name(content):
if content:
content = json.loads(content)
if (content and content.get("disabled", "No") == "No") \
or frappe.local.flags.allow_unverified_charts:
charts.append(content["name"])
if (
content and content.get("disabled", "No") == "No"
) or frappe.local.flags.allow_unverified_charts:
charts.append(content["name"])
country_code = frappe.db.get_value("Country", country, "code")
if country_code:
@@ -152,11 +174,21 @@ def get_charts_for_country(country, with_standard=False):
def get_account_tree_from_existing_company(existing_company):
all_accounts = frappe.get_all('Account',
filters={'company': existing_company},
fields = ["name", "account_name", "parent_account", "account_type",
"is_group", "root_type", "tax_rate", "account_number"],
order_by="lft, rgt")
all_accounts = frappe.get_all(
"Account",
filters={"company": existing_company},
fields=[
"name",
"account_name",
"parent_account",
"account_type",
"is_group",
"root_type",
"tax_rate",
"account_number",
],
order_by="lft, rgt",
)
account_tree = {}
@@ -165,6 +197,7 @@ def get_account_tree_from_existing_company(existing_company):
build_account_tree(account_tree, None, all_accounts)
return account_tree
def build_account_tree(tree, parent, all_accounts):
# find children
parent_account = parent.name if parent else ""
@@ -193,27 +226,29 @@ def build_account_tree(tree, parent, all_accounts):
# call recursively to build a subtree for current account
build_account_tree(tree[child.account_name], child, all_accounts)
@frappe.whitelist()
def validate_bank_account(coa, bank_account):
accounts = []
chart = get_chart(coa)
if chart:
def _get_account_names(account_master):
for account_name, child in iteritems(account_master):
if account_name not in ["account_number", "account_type",
"root_type", "is_group", "tax_rate"]:
if account_name not in ["account_number", "account_type", "root_type", "is_group", "tax_rate"]:
accounts.append(account_name)
_get_account_names(child)
_get_account_names(chart)
return (bank_account in accounts)
return bank_account in accounts
@frappe.whitelist()
def build_tree_from_json(chart_template, chart_data=None, from_coa_importer=False):
''' get chart template from its folder and parse the json to be rendered as tree '''
"""get chart template from its folder and parse the json to be rendered as tree"""
chart = chart_data or get_chart(chart_template)
# if no template selected, return as it is
@@ -221,22 +256,33 @@ def build_tree_from_json(chart_template, chart_data=None, from_coa_importer=Fals
return
accounts = []
def _import_accounts(children, parent):
''' recursively called to form a parent-child based list of dict from chart template '''
"""recursively called to form a parent-child based list of dict from chart template"""
for account_name, child in iteritems(children):
account = {}
if account_name in ["account_name", "account_number", "account_type",\
"root_type", "is_group", "tax_rate"]: continue
if account_name in [
"account_name",
"account_number",
"account_type",
"root_type",
"is_group",
"tax_rate",
]:
continue
if from_coa_importer:
account_name = child['account_name']
account_name = child["account_name"]
account['parent_account'] = parent
account['expandable'] = True if identify_is_group(child) else False
account['value'] = (cstr(child.get('account_number')).strip() + ' - ' + account_name) \
if child.get('account_number') else account_name
account["parent_account"] = parent
account["expandable"] = True if identify_is_group(child) else False
account["value"] = (
(cstr(child.get("account_number")).strip() + " - " + account_name)
if child.get("account_number")
else account_name
)
accounts.append(account)
_import_accounts(child, account['value'])
_import_accounts(child, account["value"])
_import_accounts(chart, None)
return accounts

View File

@@ -21,6 +21,7 @@ charts = {}
all_account_types = []
all_roots = {}
def go():
global accounts, charts
default_account_types = get_default_account_types()
@@ -35,14 +36,16 @@ def go():
accounts, charts = {}, {}
country_path = os.path.join(path, country_dir)
manifest = ast.literal_eval(open(os.path.join(country_path, "__openerp__.py")).read())
data_files = manifest.get("data", []) + manifest.get("init_xml", []) + \
manifest.get("update_xml", [])
data_files = (
manifest.get("data", []) + manifest.get("init_xml", []) + manifest.get("update_xml", [])
)
files_path = [os.path.join(country_path, d) for d in data_files]
xml_roots = get_xml_roots(files_path)
csv_content = get_csv_contents(files_path)
prefix = country_dir if csv_content else None
account_types = get_account_types(xml_roots.get("account.account.type", []),
csv_content.get("account.account.type", []), prefix)
account_types = get_account_types(
xml_roots.get("account.account.type", []), csv_content.get("account.account.type", []), prefix
)
account_types.update(default_account_types)
if xml_roots:
@@ -55,12 +58,15 @@ def go():
create_all_roots_file()
def get_default_account_types():
default_types_root = []
default_types_root.append(ET.parse(os.path.join(path, "account", "data",
"data_account_type.xml")).getroot())
default_types_root.append(
ET.parse(os.path.join(path, "account", "data", "data_account_type.xml")).getroot()
)
return get_account_types(default_types_root, None, prefix="account")
def get_xml_roots(files_path):
xml_roots = frappe._dict()
for filepath in files_path:
@@ -69,64 +75,69 @@ def get_xml_roots(files_path):
tree = ET.parse(filepath)
root = tree.getroot()
for node in root[0].findall("record"):
if node.get("model") in ["account.account.template",
"account.chart.template", "account.account.type"]:
if node.get("model") in [
"account.account.template",
"account.chart.template",
"account.account.type",
]:
xml_roots.setdefault(node.get("model"), []).append(root)
break
return xml_roots
def get_csv_contents(files_path):
csv_content = {}
for filepath in files_path:
fname = os.path.basename(filepath)
for file_type in ["account.account.template", "account.account.type",
"account.chart.template"]:
for file_type in ["account.account.template", "account.account.type", "account.chart.template"]:
if fname.startswith(file_type) and fname.endswith(".csv"):
with open(filepath, "r") as csvfile:
try:
csv_content.setdefault(file_type, [])\
.append(read_csv_content(csvfile.read()))
csv_content.setdefault(file_type, []).append(read_csv_content(csvfile.read()))
except Exception as e:
continue
return csv_content
def get_account_types(root_list, csv_content, prefix=None):
types = {}
account_type_map = {
'cash': 'Cash',
'bank': 'Bank',
'tr_cash': 'Cash',
'tr_bank': 'Bank',
'receivable': 'Receivable',
'tr_receivable': 'Receivable',
'account rec': 'Receivable',
'payable': 'Payable',
'tr_payable': 'Payable',
'equity': 'Equity',
'stocks': 'Stock',
'stock': 'Stock',
'tax': 'Tax',
'tr_tax': 'Tax',
'tax-out': 'Tax',
'tax-in': 'Tax',
'charges_personnel': 'Chargeable',
'fixed asset': 'Fixed Asset',
'cogs': 'Cost of Goods Sold',
"cash": "Cash",
"bank": "Bank",
"tr_cash": "Cash",
"tr_bank": "Bank",
"receivable": "Receivable",
"tr_receivable": "Receivable",
"account rec": "Receivable",
"payable": "Payable",
"tr_payable": "Payable",
"equity": "Equity",
"stocks": "Stock",
"stock": "Stock",
"tax": "Tax",
"tr_tax": "Tax",
"tax-out": "Tax",
"tax-in": "Tax",
"charges_personnel": "Chargeable",
"fixed asset": "Fixed Asset",
"cogs": "Cost of Goods Sold",
}
for root in root_list:
for node in root[0].findall("record"):
if node.get("model")=="account.account.type":
if node.get("model") == "account.account.type":
data = {}
for field in node.findall("field"):
if field.get("name")=="code" and field.text.lower() != "none" \
and account_type_map.get(field.text):
data["account_type"] = account_type_map[field.text]
if (
field.get("name") == "code"
and field.text.lower() != "none"
and account_type_map.get(field.text)
):
data["account_type"] = account_type_map[field.text]
node_id = prefix + "." + node.get("id") if prefix else node.get("id")
types[node_id] = data
if csv_content and csv_content[0][0]=="id":
if csv_content and csv_content[0][0] == "id":
for row in csv_content[1:]:
row_dict = dict(zip(csv_content[0], row))
data = {}
@@ -137,21 +148,22 @@ def get_account_types(root_list, csv_content, prefix=None):
types[node_id] = data
return types
def make_maps_for_xml(xml_roots, account_types, country_dir):
"""make maps for `charts` and `accounts`"""
for model, root_list in iteritems(xml_roots):
for root in root_list:
for node in root[0].findall("record"):
if node.get("model")=="account.account.template":
if node.get("model") == "account.account.template":
data = {}
for field in node.findall("field"):
if field.get("name")=="name":
if field.get("name") == "name":
data["name"] = field.text
if field.get("name")=="parent_id":
if field.get("name") == "parent_id":
parent_id = field.get("ref") or field.get("eval")
data["parent_id"] = parent_id
if field.get("name")=="user_type":
if field.get("name") == "user_type":
value = field.get("ref")
if account_types.get(value, {}).get("account_type"):
data["account_type"] = account_types[value]["account_type"]
@@ -161,16 +173,17 @@ def make_maps_for_xml(xml_roots, account_types, country_dir):
data["children"] = []
accounts[node.get("id")] = data
if node.get("model")=="account.chart.template":
if node.get("model") == "account.chart.template":
data = {}
for field in node.findall("field"):
if field.get("name")=="name":
if field.get("name") == "name":
data["name"] = field.text
if field.get("name")=="account_root_id":
if field.get("name") == "account_root_id":
data["account_root_id"] = field.get("ref")
data["id"] = country_dir
charts.setdefault(node.get("id"), {}).update(data)
def make_maps_for_csv(csv_content, account_types, country_dir):
for content in csv_content.get("account.account.template", []):
for row in content[1:]:
@@ -178,7 +191,7 @@ def make_maps_for_csv(csv_content, account_types, country_dir):
account = {
"name": data.get("name"),
"parent_id": data.get("parent_id:id") or data.get("parent_id/id"),
"children": []
"children": [],
}
user_type = data.get("user_type/id") or data.get("user_type:id")
if account_types.get(user_type, {}).get("account_type"):
@@ -195,12 +208,14 @@ def make_maps_for_csv(csv_content, account_types, country_dir):
for row in content[1:]:
if row:
data = dict(zip(content[0], row))
charts.setdefault(data.get("id"), {}).update({
"account_root_id": data.get("account_root_id:id") or \
data.get("account_root_id/id"),
"name": data.get("name"),
"id": country_dir
})
charts.setdefault(data.get("id"), {}).update(
{
"account_root_id": data.get("account_root_id:id") or data.get("account_root_id/id"),
"name": data.get("name"),
"id": country_dir,
}
)
def make_account_trees():
"""build tree hierarchy"""
@@ -219,6 +234,7 @@ def make_account_trees():
if "children" in accounts[id] and not accounts[id].get("children"):
del accounts[id]["children"]
def make_charts():
"""write chart files in app/setup/doctype/company/charts"""
for chart_id in charts:
@@ -237,34 +253,38 @@ def make_charts():
chart["country_code"] = src["id"][5:]
chart["tree"] = accounts[src["account_root_id"]]
for key, val in chart["tree"].items():
if key in ["name", "parent_id"]:
chart["tree"].pop(key)
if type(val) == dict:
val["root_type"] = ""
if chart:
fpath = os.path.join("erpnext", "erpnext", "accounts", "doctype", "account",
"chart_of_accounts", filename + ".json")
fpath = os.path.join(
"erpnext", "erpnext", "accounts", "doctype", "account", "chart_of_accounts", filename + ".json"
)
with open(fpath, "r") as chartfile:
old_content = chartfile.read()
if not old_content or (json.loads(old_content).get("is_active", "No") == "No" \
and json.loads(old_content).get("disabled", "No") == "No"):
if not old_content or (
json.loads(old_content).get("is_active", "No") == "No"
and json.loads(old_content).get("disabled", "No") == "No"
):
with open(fpath, "w") as chartfile:
chartfile.write(json.dumps(chart, indent=4, sort_keys=True))
all_roots.setdefault(filename, chart["tree"].keys())
def create_all_roots_file():
with open('all_roots.txt', 'w') as f:
with open("all_roots.txt", "w") as f:
for filename, roots in sorted(all_roots.items()):
f.write(filename)
f.write('\n----------------------\n')
f.write("\n----------------------\n")
for r in sorted(roots):
f.write(r.encode('utf-8'))
f.write('\n')
f.write('\n\n\n')
f.write(r.encode("utf-8"))
f.write("\n")
f.write("\n\n\n")
if __name__=="__main__":
if __name__ == "__main__":
go()

View File

@@ -7,182 +7,103 @@ from frappe import _
def get():
return {
_("Application of Funds (Assets)"): {
_("Current Assets"): {
_("Accounts Receivable"): {
_("Debtors"): {
"account_type": "Receivable"
}
},
_("Bank Accounts"): {
"account_type": "Bank",
"is_group": 1
},
_("Cash In Hand"): {
_("Cash"): {
"account_type": "Cash"
},
"account_type": "Cash"
},
_("Loans and Advances (Assets)"): {
_("Employee Advances"): {
},
},
_("Securities and Deposits"): {
_("Earnest Money"): {}
},
_("Stock Assets"): {
_("Stock In Hand"): {
"account_type": "Stock"
},
"account_type": "Stock",
},
_("Tax Assets"): {
"is_group": 1
}
},
_("Fixed Assets"): {
_("Capital Equipments"): {
"account_type": "Fixed Asset"
},
_("Electronic Equipments"): {
"account_type": "Fixed Asset"
},
_("Furnitures and Fixtures"): {
"account_type": "Fixed Asset"
},
_("Office Equipments"): {
"account_type": "Fixed Asset"
},
_("Plants and Machineries"): {
"account_type": "Fixed Asset"
},
_("Buildings"): {
"account_type": "Fixed Asset"
_("Application of Funds (Assets)"): {
_("Current Assets"): {
_("Accounts Receivable"): {_("Debtors"): {"account_type": "Receivable"}},
_("Bank Accounts"): {"account_type": "Bank", "is_group": 1},
_("Cash In Hand"): {_("Cash"): {"account_type": "Cash"}, "account_type": "Cash"},
_("Loans and Advances (Assets)"): {
_("Employee Advances"): {},
},
_("Softwares"): {
"account_type": "Fixed Asset"
_("Securities and Deposits"): {_("Earnest Money"): {}},
_("Stock Assets"): {
_("Stock In Hand"): {"account_type": "Stock"},
"account_type": "Stock",
},
_("Accumulated Depreciation"): {
"account_type": "Accumulated Depreciation"
},
_("CWIP Account"): {
"account_type": "Capital Work in Progress",
}
},
_("Investments"): {
"is_group": 1
},
_("Temporary Accounts"): {
_("Temporary Opening"): {
"account_type": "Temporary"
}
},
"root_type": "Asset"
},
_("Expenses"): {
_("Direct Expenses"): {
_("Stock Expenses"): {
_("Cost of Goods Sold"): {
"account_type": "Cost of Goods Sold"
},
_("Expenses Included In Asset Valuation"): {
"account_type": "Expenses Included In Asset Valuation"
},
_("Expenses Included In Valuation"): {
"account_type": "Expenses Included In Valuation"
},
_("Stock Adjustment"): {
"account_type": "Stock Adjustment"
}
},
},
_("Indirect Expenses"): {
_("Administrative Expenses"): {},
_("Commission on Sales"): {},
_("Depreciation"): {
"account_type": "Depreciation"
},
_("Entertainment Expenses"): {},
_("Freight and Forwarding Charges"): {
"account_type": "Chargeable"
},
_("Legal Expenses"): {},
_("Marketing Expenses"): {
"account_type": "Chargeable"
},
_("Miscellaneous Expenses"): {
"account_type": "Chargeable"
},
_("Office Maintenance Expenses"): {},
_("Office Rent"): {},
_("Postal Expenses"): {},
_("Print and Stationery"): {},
_("Round Off"): {
"account_type": "Round Off"
},
_("Salary"): {},
_("Sales Expenses"): {},
_("Telephone Expenses"): {},
_("Travel Expenses"): {},
_("Utility Expenses"): {},
_("Tax Assets"): {"is_group": 1},
},
_("Fixed Assets"): {
_("Capital Equipments"): {"account_type": "Fixed Asset"},
_("Electronic Equipments"): {"account_type": "Fixed Asset"},
_("Furnitures and Fixtures"): {"account_type": "Fixed Asset"},
_("Office Equipments"): {"account_type": "Fixed Asset"},
_("Plants and Machineries"): {"account_type": "Fixed Asset"},
_("Buildings"): {"account_type": "Fixed Asset"},
_("Softwares"): {"account_type": "Fixed Asset"},
_("Accumulated Depreciation"): {"account_type": "Accumulated Depreciation"},
_("CWIP Account"): {
"account_type": "Capital Work in Progress",
},
},
_("Investments"): {"is_group": 1},
_("Temporary Accounts"): {_("Temporary Opening"): {"account_type": "Temporary"}},
"root_type": "Asset",
},
_("Expenses"): {
_("Direct Expenses"): {
_("Stock Expenses"): {
_("Cost of Goods Sold"): {"account_type": "Cost of Goods Sold"},
_("Expenses Included In Asset Valuation"): {
"account_type": "Expenses Included In Asset Valuation"
},
_("Expenses Included In Valuation"): {"account_type": "Expenses Included In Valuation"},
_("Stock Adjustment"): {"account_type": "Stock Adjustment"},
},
},
_("Indirect Expenses"): {
_("Administrative Expenses"): {},
_("Commission on Sales"): {},
_("Depreciation"): {"account_type": "Depreciation"},
_("Entertainment Expenses"): {},
_("Freight and Forwarding Charges"): {"account_type": "Chargeable"},
_("Legal Expenses"): {},
_("Marketing Expenses"): {"account_type": "Chargeable"},
_("Miscellaneous Expenses"): {"account_type": "Chargeable"},
_("Office Maintenance Expenses"): {},
_("Office Rent"): {},
_("Postal Expenses"): {},
_("Print and Stationery"): {},
_("Round Off"): {"account_type": "Round Off"},
_("Salary"): {},
_("Sales Expenses"): {},
_("Telephone Expenses"): {},
_("Travel Expenses"): {},
_("Utility Expenses"): {},
_("Write Off"): {},
_("Exchange Gain/Loss"): {},
_("Gain/Loss on Asset Disposal"): {}
},
"root_type": "Expense"
},
_("Income"): {
_("Direct Income"): {
_("Sales"): {},
_("Service"): {}
},
_("Indirect Income"): {
"is_group": 1
},
"root_type": "Income"
},
_("Source of Funds (Liabilities)"): {
_("Current Liabilities"): {
_("Accounts Payable"): {
_("Creditors"): {
"account_type": "Payable"
},
_("Payroll Payable"): {},
},
_("Stock Liabilities"): {
_("Stock Received But Not Billed"): {
"account_type": "Stock Received But Not Billed"
},
_("Asset Received But Not Billed"): {
"account_type": "Asset Received But Not Billed"
}
},
_("Duties and Taxes"): {
"account_type": "Tax",
"is_group": 1
_("Gain/Loss on Asset Disposal"): {},
},
"root_type": "Expense",
},
_("Income"): {
_("Direct Income"): {_("Sales"): {}, _("Service"): {}},
_("Indirect Income"): {"is_group": 1},
"root_type": "Income",
},
_("Source of Funds (Liabilities)"): {
_("Current Liabilities"): {
_("Accounts Payable"): {
_("Creditors"): {"account_type": "Payable"},
_("Payroll Payable"): {},
},
_("Stock Liabilities"): {
_("Stock Received But Not Billed"): {"account_type": "Stock Received But Not Billed"},
_("Asset Received But Not Billed"): {"account_type": "Asset Received But Not Billed"},
},
_("Duties and Taxes"): {"account_type": "Tax", "is_group": 1},
_("Loans (Liabilities)"): {
_("Secured Loans"): {},
_("Unsecured Loans"): {},
_("Bank Overdraft Account"): {},
},
},
"root_type": "Liability"
},
},
"root_type": "Liability",
},
_("Equity"): {
_("Capital Stock"): {
"account_type": "Equity"
},
_("Dividends Paid"): {
"account_type": "Equity"
},
_("Opening Balance Equity"): {
"account_type": "Equity"
},
_("Retained Earnings"): {
"account_type": "Equity"
},
"root_type": "Equity"
}
_("Capital Stock"): {"account_type": "Equity"},
_("Dividends Paid"): {"account_type": "Equity"},
_("Opening Balance Equity"): {"account_type": "Equity"},
_("Retained Earnings"): {"account_type": "Equity"},
"root_type": "Equity",
},
}

View File

@@ -6,288 +6,153 @@ from frappe import _
def get():
return {
_("Application of Funds (Assets)"): {
_("Current Assets"): {
_("Accounts Receivable"): {
_("Debtors"): {
"account_type": "Receivable",
"account_number": "1310"
},
"account_number": "1300"
},
_("Bank Accounts"): {
"account_type": "Bank",
"is_group": 1,
"account_number": "1200"
},
_("Cash In Hand"): {
_("Cash"): {
"account_type": "Cash",
"account_number": "1110"
},
"account_type": "Cash",
"account_number": "1100"
},
_("Loans and Advances (Assets)"): {
_("Employee Advances"): {
"account_number": "1610"
},
"account_number": "1600"
},
_("Securities and Deposits"): {
_("Earnest Money"): {
"account_number": "1651"
},
"account_number": "1650"
},
_("Stock Assets"): {
_("Stock In Hand"): {
"account_type": "Stock",
"account_number": "1410"
},
"account_type": "Stock",
"account_number": "1400"
},
_("Tax Assets"): {
"is_group": 1,
"account_number": "1500"
},
"account_number": "1100-1600"
},
_("Fixed Assets"): {
_("Capital Equipments"): {
"account_type": "Fixed Asset",
"account_number": "1710"
},
_("Electronic Equipments"): {
"account_type": "Fixed Asset",
"account_number": "1720"
},
_("Furnitures and Fixtures"): {
"account_type": "Fixed Asset",
"account_number": "1730"
},
_("Office Equipments"): {
"account_type": "Fixed Asset",
"account_number": "1740"
},
_("Plants and Machineries"): {
"account_type": "Fixed Asset",
"account_number": "1750"
},
_("Buildings"): {
"account_type": "Fixed Asset",
"account_number": "1760"
},
_("Softwares"): {
"account_type": "Fixed Asset",
"account_number": "1770"
},
_("Accumulated Depreciation"): {
"account_type": "Accumulated Depreciation",
"account_number": "1780"
},
_("CWIP Account"): {
"account_type": "Capital Work in Progress",
"account_number": "1790"
},
"account_number": "1700"
},
_("Investments"): {
"is_group": 1,
"account_number": "1800"
},
_("Temporary Accounts"): {
_("Temporary Opening"): {
"account_type": "Temporary",
"account_number": "1910"
},
"account_number": "1900"
},
"root_type": "Asset",
"account_number": "1000"
},
_("Expenses"): {
_("Direct Expenses"): {
_("Stock Expenses"): {
_("Cost of Goods Sold"): {
"account_type": "Cost of Goods Sold",
"account_number": "5111"
},
_("Expenses Included In Asset Valuation"): {
"account_type": "Expenses Included In Asset Valuation",
"account_number": "5112"
},
_("Expenses Included In Valuation"): {
"account_type": "Expenses Included In Valuation",
"account_number": "5118"
},
_("Stock Adjustment"): {
"account_type": "Stock Adjustment",
"account_number": "5119"
},
"account_number": "5110"
},
"account_number": "5100"
},
_("Indirect Expenses"): {
_("Administrative Expenses"): {
"account_number": "5201"
},
_("Commission on Sales"): {
"account_number": "5202"
},
_("Depreciation"): {
"account_type": "Depreciation",
"account_number": "5203"
},
_("Entertainment Expenses"): {
"account_number": "5204"
},
_("Freight and Forwarding Charges"): {
"account_type": "Chargeable",
"account_number": "5205"
},
_("Legal Expenses"): {
"account_number": "5206"
},
_("Marketing Expenses"): {
"account_type": "Chargeable",
"account_number": "5207"
},
_("Office Maintenance Expenses"): {
"account_number": "5208"
},
_("Office Rent"): {
"account_number": "5209"
},
_("Postal Expenses"): {
"account_number": "5210"
},
_("Print and Stationery"): {
"account_number": "5211"
},
_("Round Off"): {
"account_type": "Round Off",
"account_number": "5212"
},
_("Salary"): {
"account_number": "5213"
},
_("Sales Expenses"): {
"account_number": "5214"
},
_("Telephone Expenses"): {
"account_number": "5215"
},
_("Travel Expenses"): {
"account_number": "5216"
},
_("Utility Expenses"): {
"account_number": "5217"
},
_("Write Off"): {
"account_number": "5218"
},
_("Exchange Gain/Loss"): {
"account_number": "5219"
},
_("Gain/Loss on Asset Disposal"): {
"account_number": "5220"
},
_("Miscellaneous Expenses"): {
"account_type": "Chargeable",
"account_number": "5221"
},
"account_number": "5200"
},
"root_type": "Expense",
"account_number": "5000"
},
_("Income"): {
_("Direct Income"): {
_("Sales"): {
"account_number": "4110"
},
_("Service"): {
"account_number": "4120"
},
"account_number": "4100"
},
_("Indirect Income"): {
"is_group": 1,
"account_number": "4200"
},
"root_type": "Income",
"account_number": "4000"
},
_("Source of Funds (Liabilities)"): {
_("Current Liabilities"): {
_("Accounts Payable"): {
_("Creditors"): {
"account_type": "Payable",
"account_number": "2110"
},
_("Payroll Payable"): {
"account_number": "2120"
},
"account_number": "2100"
},
_("Stock Liabilities"): {
_("Stock Received But Not Billed"): {
"account_type": "Stock Received But Not Billed",
"account_number": "2210"
},
_("Asset Received But Not Billed"): {
"account_type": "Asset Received But Not Billed",
"account_number": "2211"
},
"account_number": "2200"
},
_("Duties and Taxes"): {
_("TDS Payable"): {
"account_number": "2310"
},
"account_type": "Tax",
"is_group": 1,
"account_number": "2300"
},
_("Loans (Liabilities)"): {
_("Secured Loans"): {
"account_number": "2410"
},
_("Unsecured Loans"): {
"account_number": "2420"
},
_("Bank Overdraft Account"): {
"account_number": "2430"
},
"account_number": "2400"
},
"account_number": "2100-2400"
},
"root_type": "Liability",
"account_number": "2000"
},
_("Equity"): {
_("Capital Stock"): {
"account_type": "Equity",
"account_number": "3100"
},
_("Dividends Paid"): {
"account_type": "Equity",
"account_number": "3200"
},
_("Opening Balance Equity"): {
"account_type": "Equity",
"account_number": "3300"
},
_("Retained Earnings"): {
"account_type": "Equity",
"account_number": "3400"
},
"root_type": "Equity",
"account_number": "3000"
}
}
return {
_("Application of Funds (Assets)"): {
_("Current Assets"): {
_("Accounts Receivable"): {
_("Debtors"): {"account_type": "Receivable", "account_number": "1310"},
"account_number": "1300",
},
_("Bank Accounts"): {"account_type": "Bank", "is_group": 1, "account_number": "1200"},
_("Cash In Hand"): {
_("Cash"): {"account_type": "Cash", "account_number": "1110"},
"account_type": "Cash",
"account_number": "1100",
},
_("Loans and Advances (Assets)"): {
_("Employee Advances"): {"account_number": "1610"},
"account_number": "1600",
},
_("Securities and Deposits"): {
_("Earnest Money"): {"account_number": "1651"},
"account_number": "1650",
},
_("Stock Assets"): {
_("Stock In Hand"): {"account_type": "Stock", "account_number": "1410"},
"account_type": "Stock",
"account_number": "1400",
},
_("Tax Assets"): {"is_group": 1, "account_number": "1500"},
"account_number": "1100-1600",
},
_("Fixed Assets"): {
_("Capital Equipments"): {"account_type": "Fixed Asset", "account_number": "1710"},
_("Electronic Equipments"): {"account_type": "Fixed Asset", "account_number": "1720"},
_("Furnitures and Fixtures"): {"account_type": "Fixed Asset", "account_number": "1730"},
_("Office Equipments"): {"account_type": "Fixed Asset", "account_number": "1740"},
_("Plants and Machineries"): {"account_type": "Fixed Asset", "account_number": "1750"},
_("Buildings"): {"account_type": "Fixed Asset", "account_number": "1760"},
_("Softwares"): {"account_type": "Fixed Asset", "account_number": "1770"},
_("Accumulated Depreciation"): {
"account_type": "Accumulated Depreciation",
"account_number": "1780",
},
_("CWIP Account"): {"account_type": "Capital Work in Progress", "account_number": "1790"},
"account_number": "1700",
},
_("Investments"): {"is_group": 1, "account_number": "1800"},
_("Temporary Accounts"): {
_("Temporary Opening"): {"account_type": "Temporary", "account_number": "1910"},
"account_number": "1900",
},
"root_type": "Asset",
"account_number": "1000",
},
_("Expenses"): {
_("Direct Expenses"): {
_("Stock Expenses"): {
_("Cost of Goods Sold"): {"account_type": "Cost of Goods Sold", "account_number": "5111"},
_("Expenses Included In Asset Valuation"): {
"account_type": "Expenses Included In Asset Valuation",
"account_number": "5112",
},
_("Expenses Included In Valuation"): {
"account_type": "Expenses Included In Valuation",
"account_number": "5118",
},
_("Stock Adjustment"): {"account_type": "Stock Adjustment", "account_number": "5119"},
"account_number": "5110",
},
"account_number": "5100",
},
_("Indirect Expenses"): {
_("Administrative Expenses"): {"account_number": "5201"},
_("Commission on Sales"): {"account_number": "5202"},
_("Depreciation"): {"account_type": "Depreciation", "account_number": "5203"},
_("Entertainment Expenses"): {"account_number": "5204"},
_("Freight and Forwarding Charges"): {"account_type": "Chargeable", "account_number": "5205"},
_("Legal Expenses"): {"account_number": "5206"},
_("Marketing Expenses"): {"account_type": "Chargeable", "account_number": "5207"},
_("Office Maintenance Expenses"): {"account_number": "5208"},
_("Office Rent"): {"account_number": "5209"},
_("Postal Expenses"): {"account_number": "5210"},
_("Print and Stationery"): {"account_number": "5211"},
_("Round Off"): {"account_type": "Round Off", "account_number": "5212"},
_("Salary"): {"account_number": "5213"},
_("Sales Expenses"): {"account_number": "5214"},
_("Telephone Expenses"): {"account_number": "5215"},
_("Travel Expenses"): {"account_number": "5216"},
_("Utility Expenses"): {"account_number": "5217"},
_("Write Off"): {"account_number": "5218"},
_("Exchange Gain/Loss"): {"account_number": "5219"},
_("Gain/Loss on Asset Disposal"): {"account_number": "5220"},
_("Miscellaneous Expenses"): {"account_type": "Chargeable", "account_number": "5221"},
"account_number": "5200",
},
"root_type": "Expense",
"account_number": "5000",
},
_("Income"): {
_("Direct Income"): {
_("Sales"): {"account_number": "4110"},
_("Service"): {"account_number": "4120"},
"account_number": "4100",
},
_("Indirect Income"): {"is_group": 1, "account_number": "4200"},
"root_type": "Income",
"account_number": "4000",
},
_("Source of Funds (Liabilities)"): {
_("Current Liabilities"): {
_("Accounts Payable"): {
_("Creditors"): {"account_type": "Payable", "account_number": "2110"},
_("Payroll Payable"): {"account_number": "2120"},
"account_number": "2100",
},
_("Stock Liabilities"): {
_("Stock Received But Not Billed"): {
"account_type": "Stock Received But Not Billed",
"account_number": "2210",
},
_("Asset Received But Not Billed"): {
"account_type": "Asset Received But Not Billed",
"account_number": "2211",
},
"account_number": "2200",
},
_("Duties and Taxes"): {
_("TDS Payable"): {"account_number": "2310"},
"account_type": "Tax",
"is_group": 1,
"account_number": "2300",
},
_("Loans (Liabilities)"): {
_("Secured Loans"): {"account_number": "2410"},
_("Unsecured Loans"): {"account_number": "2420"},
_("Bank Overdraft Account"): {"account_number": "2430"},
"account_number": "2400",
},
"account_number": "2100-2400",
},
"root_type": "Liability",
"account_number": "2000",
},
_("Equity"): {
_("Capital Stock"): {"account_type": "Equity", "account_number": "3100"},
_("Dividends Paid"): {"account_type": "Equity", "account_number": "3200"},
_("Opening Balance Equity"): {"account_type": "Equity", "account_number": "3300"},
_("Retained Earnings"): {"account_type": "Equity", "account_number": "3400"},
"root_type": "Equity",
"account_number": "3000",
},
}

View File

@@ -20,8 +20,9 @@ class TestAccount(unittest.TestCase):
acc.company = "_Test Company"
acc.insert()
account_number, account_name = frappe.db.get_value("Account", "1210 - Debtors - _TC",
["account_number", "account_name"])
account_number, account_name = frappe.db.get_value(
"Account", "1210 - Debtors - _TC", ["account_number", "account_name"]
)
self.assertEqual(account_number, "1210")
self.assertEqual(account_name, "Debtors")
@@ -30,8 +31,12 @@ class TestAccount(unittest.TestCase):
update_account_number("1210 - Debtors - _TC", new_account_name, new_account_number)
new_acc = frappe.db.get_value("Account", "1211-11-4 - 6 - - Debtors 1 - Test - - _TC",
["account_name", "account_number"], as_dict=1)
new_acc = frappe.db.get_value(
"Account",
"1211-11-4 - 6 - - Debtors 1 - Test - - _TC",
["account_name", "account_number"],
as_dict=1,
)
self.assertEqual(new_acc.account_name, "Debtors 1 - Test -")
self.assertEqual(new_acc.account_number, "1211-11-4 - 6 -")
@@ -79,7 +84,9 @@ class TestAccount(unittest.TestCase):
self.assertEqual(parent, "Securities and Deposits - _TC")
merge_account("Securities and Deposits - _TC", "Cash In Hand - _TC", doc.is_group, doc.root_type, doc.company)
merge_account(
"Securities and Deposits - _TC", "Cash In Hand - _TC", doc.is_group, doc.root_type, doc.company
)
parent = frappe.db.get_value("Account", "Earnest Money - _TC", "parent_account")
# Parent account of the child account changes after merging
@@ -91,14 +98,28 @@ class TestAccount(unittest.TestCase):
doc = frappe.get_doc("Account", "Current Assets - _TC")
# Raise error as is_group property doesn't match
self.assertRaises(frappe.ValidationError, merge_account, "Current Assets - _TC",\
"Accumulated Depreciation - _TC", doc.is_group, doc.root_type, doc.company)
self.assertRaises(
frappe.ValidationError,
merge_account,
"Current Assets - _TC",
"Accumulated Depreciation - _TC",
doc.is_group,
doc.root_type,
doc.company,
)
doc = frappe.get_doc("Account", "Capital Stock - _TC")
# Raise error as root_type property doesn't match
self.assertRaises(frappe.ValidationError, merge_account, "Capital Stock - _TC",\
"Softwares - _TC", doc.is_group, doc.root_type, doc.company)
self.assertRaises(
frappe.ValidationError,
merge_account,
"Capital Stock - _TC",
"Softwares - _TC",
doc.is_group,
doc.root_type,
doc.company,
)
def test_account_sync(self):
frappe.local.flags.pop("ignore_root_company_validation", None)
@@ -109,8 +130,12 @@ class TestAccount(unittest.TestCase):
acc.company = "_Test Company 3"
acc.insert()
acc_tc_4 = frappe.db.get_value('Account', {'account_name': "Test Sync Account", "company": "_Test Company 4"})
acc_tc_5 = frappe.db.get_value('Account', {'account_name': "Test Sync Account", "company": "_Test Company 5"})
acc_tc_4 = frappe.db.get_value(
"Account", {"account_name": "Test Sync Account", "company": "_Test Company 4"}
)
acc_tc_5 = frappe.db.get_value(
"Account", {"account_name": "Test Sync Account", "company": "_Test Company 5"}
)
self.assertEqual(acc_tc_4, "Test Sync Account - _TC4")
self.assertEqual(acc_tc_5, "Test Sync Account - _TC5")
@@ -138,8 +163,26 @@ class TestAccount(unittest.TestCase):
update_account_number(acc.name, "Test Rename Sync Account", "1234")
# Check if renamed in children
self.assertTrue(frappe.db.exists("Account", {'account_name': "Test Rename Sync Account", "company": "_Test Company 4", "account_number": "1234"}))
self.assertTrue(frappe.db.exists("Account", {'account_name': "Test Rename Sync Account", "company": "_Test Company 5", "account_number": "1234"}))
self.assertTrue(
frappe.db.exists(
"Account",
{
"account_name": "Test Rename Sync Account",
"company": "_Test Company 4",
"account_number": "1234",
},
)
)
self.assertTrue(
frappe.db.exists(
"Account",
{
"account_name": "Test Rename Sync Account",
"company": "_Test Company 5",
"account_number": "1234",
},
)
)
frappe.delete_doc("Account", "1234 - Test Rename Sync Account - _TC3")
frappe.delete_doc("Account", "1234 - Test Rename Sync Account - _TC4")
@@ -155,25 +198,71 @@ class TestAccount(unittest.TestCase):
acc.company = "_Test Company 3"
acc.insert()
self.assertTrue(frappe.db.exists("Account", {'account_name': "Test Group Account", "company": "_Test Company 4"}))
self.assertTrue(frappe.db.exists("Account", {'account_name': "Test Group Account", "company": "_Test Company 5"}))
self.assertTrue(
frappe.db.exists(
"Account", {"account_name": "Test Group Account", "company": "_Test Company 4"}
)
)
self.assertTrue(
frappe.db.exists(
"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
)
)
# Try renaming child company account
acc_tc_5 = frappe.db.get_value('Account', {'account_name': "Test Group Account", "company": "_Test Company 5"})
self.assertRaises(frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account")
acc_tc_5 = frappe.db.get_value(
"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
)
self.assertRaises(
frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account"
)
# Rename child company account with allow_account_creation_against_child_company enabled
frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 1)
frappe.db.set_value(
"Company", "_Test Company 5", "allow_account_creation_against_child_company", 1
)
update_account_number(acc_tc_5, "Test Modified Account")
self.assertTrue(frappe.db.exists("Account", {'name': "Test Modified Account - _TC5", "company": "_Test Company 5"}))
self.assertTrue(
frappe.db.exists(
"Account", {"name": "Test Modified Account - _TC5", "company": "_Test Company 5"}
)
)
frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 0)
frappe.db.set_value(
"Company", "_Test Company 5", "allow_account_creation_against_child_company", 0
)
to_delete = ["Test Group Account - _TC3", "Test Group Account - _TC4", "Test Modified Account - _TC5"]
to_delete = [
"Test Group Account - _TC3",
"Test Group Account - _TC4",
"Test Modified Account - _TC5",
]
for doc in to_delete:
frappe.delete_doc("Account", doc)
def test_validate_account_currency(self):
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
if not frappe.db.get_value("Account", "Test Currency Account - _TC"):
acc = frappe.new_doc("Account")
acc.account_name = "Test Currency Account"
acc.parent_account = "Tax Assets - _TC"
acc.company = "_Test Company"
acc.insert()
else:
acc = frappe.get_doc("Account", "Test Currency Account - _TC")
self.assertEqual(acc.account_currency, "INR")
# Make a JV against this account
make_journal_entry(
"Test Currency Account - _TC", "Miscellaneous Expenses - _TC", 100, submit=True
)
acc.account_currency = "USD"
self.assertRaises(frappe.ValidationError, acc.save)
def _make_test_records(verbose=None):
from frappe.test_runner import make_test_objects
@@ -184,20 +273,16 @@ def _make_test_records(verbose=None):
["_Test Bank USD", "Bank Accounts", 0, "Bank", "USD"],
["_Test Bank EUR", "Bank Accounts", 0, "Bank", "EUR"],
["_Test Cash", "Cash In Hand", 0, "Cash", None],
["_Test Account Stock Expenses", "Direct Expenses", 1, None, None],
["_Test Account Shipping Charges", "_Test Account Stock Expenses", 0, "Chargeable", None],
["_Test Account Customs Duty", "_Test Account Stock Expenses", 0, "Tax", None],
["_Test Account Insurance Charges", "_Test Account Stock Expenses", 0, "Chargeable", None],
["_Test Account Stock Adjustment", "_Test Account Stock Expenses", 0, "Stock Adjustment", None],
["_Test Employee Advance", "Current Liabilities", 0, None, None],
["_Test Account Tax Assets", "Current Assets", 1, None, None],
["_Test Account VAT", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Service Tax", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Reserves and Surplus", "Current Liabilities", 0, None, None],
["_Test Account Cost for Goods Sold", "Expenses", 0, None, None],
["_Test Account Excise Duty", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Education Cess", "_Test Account Tax Assets", 0, "Tax", None],
@@ -206,38 +291,45 @@ def _make_test_records(verbose=None):
["_Test Account Discount", "Direct Expenses", 0, None, None],
["_Test Write Off", "Indirect Expenses", 0, None, None],
["_Test Exchange Gain/Loss", "Indirect Expenses", 0, None, None],
["_Test Account Sales", "Direct Income", 0, None, None],
# related to Account Inventory Integration
["_Test Account Stock In Hand", "Current Assets", 0, None, None],
# fixed asset depreciation
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None],
["_Test Depreciations", "Expenses", 0, None, None],
["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
# Receivable / Payable Account
["_Test Receivable", "Current Assets", 0, "Receivable", None],
["_Test Payable", "Current Liabilities", 0, "Payable", None],
["_Test Receivable USD", "Current Assets", 0, "Receivable", "USD"],
["_Test Payable USD", "Current Liabilities", 0, "Payable", "USD"]
["_Test Payable USD", "Current Liabilities", 0, "Payable", "USD"],
]
for company, abbr in [["_Test Company", "_TC"], ["_Test Company 1", "_TC1"], ["_Test Company with perpetual inventory", "TCP1"]]:
test_objects = make_test_objects("Account", [{
"doctype": "Account",
"account_name": account_name,
"parent_account": parent_account + " - " + abbr,
"company": company,
"is_group": is_group,
"account_type": account_type,
"account_currency": currency
} for account_name, parent_account, is_group, account_type, currency in accounts])
for company, abbr in [
["_Test Company", "_TC"],
["_Test Company 1", "_TC1"],
["_Test Company with perpetual inventory", "TCP1"],
]:
test_objects = make_test_objects(
"Account",
[
{
"doctype": "Account",
"account_name": account_name,
"parent_account": parent_account + " - " + abbr,
"company": company,
"is_group": is_group,
"account_type": account_type,
"account_currency": currency,
}
for account_name, parent_account, is_group, account_type, currency in accounts
],
)
return test_objects
def get_inventory_account(company, warehouse=None):
account = None
if warehouse:
@@ -247,19 +339,24 @@ def get_inventory_account(company, warehouse=None):
return account
def create_account(**kwargs):
account = frappe.db.get_value("Account", filters={"account_name": kwargs.get("account_name"), "company": kwargs.get("company")})
account = frappe.db.get_value(
"Account", filters={"account_name": kwargs.get("account_name"), "company": kwargs.get("company")}
)
if account:
return account
else:
account = frappe.get_doc(dict(
doctype = "Account",
account_name = kwargs.get('account_name'),
account_type = kwargs.get('account_type'),
parent_account = kwargs.get('parent_account'),
company = kwargs.get('company'),
account_currency = kwargs.get('account_currency')
))
account = frappe.get_doc(
dict(
doctype="Account",
account_name=kwargs.get("account_name"),
account_type=kwargs.get("account_type"),
parent_account=kwargs.get("parent_account"),
company=kwargs.get("company"),
account_currency=kwargs.get("account_currency"),
)
)
account.save()
return account.name

View File

@@ -17,13 +17,21 @@ class AccountingDimension(Document):
self.set_fieldname_and_label()
def validate(self):
if self.document_type in core_doctypes_list + ('Accounting Dimension', 'Project',
'Cost Center', 'Accounting Dimension Detail', 'Company', 'Account') :
if self.document_type in core_doctypes_list + (
"Accounting Dimension",
"Project",
"Cost Center",
"Accounting Dimension Detail",
"Company",
"Account",
):
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
frappe.throw(msg)
exists = frappe.db.get_value("Accounting Dimension", {'document_type': self.document_type}, ['name'])
exists = frappe.db.get_value(
"Accounting Dimension", {"document_type": self.document_type}, ["name"]
)
if exists and self.is_new():
frappe.throw(_("Document Type already used as a dimension"))
@@ -42,13 +50,13 @@ class AccountingDimension(Document):
if frappe.flags.in_test:
make_dimension_in_accounting_doctypes(doc=self)
else:
frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self, queue='long')
frappe.enqueue(make_dimension_in_accounting_doctypes, doc=self, queue="long")
def on_trash(self):
if frappe.flags.in_test:
delete_accounting_dimension(doc=self)
else:
frappe.enqueue(delete_accounting_dimension, doc=self, queue='long')
frappe.enqueue(delete_accounting_dimension, doc=self, queue="long")
def set_fieldname_and_label(self):
if not self.label:
@@ -60,6 +68,7 @@ class AccountingDimension(Document):
def on_update(self):
frappe.flags.accounting_dimensions = None
def make_dimension_in_accounting_doctypes(doc, doclist=None):
if not doclist:
doclist = get_doctypes_with_dimensions()
@@ -70,9 +79,9 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
for doctype in doclist:
if (doc_count + 1) % 2 == 0:
insert_after_field = 'dimension_col_break'
insert_after_field = "dimension_col_break"
else:
insert_after_field = 'accounting_dimensions_section'
insert_after_field = "accounting_dimensions_section"
df = {
"fieldname": doc.fieldname,
@@ -80,30 +89,33 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
"fieldtype": "Link",
"options": doc.document_type,
"insert_after": insert_after_field,
"owner": "Administrator"
"owner": "Administrator",
}
meta = frappe.get_meta(doctype, cached=False)
fieldnames = [d.fieldname for d in meta.get("fields")]
if df['fieldname'] not in fieldnames:
if df["fieldname"] not in fieldnames:
if doctype == "Budget":
add_dimension_to_budget_doctype(df.copy(), doc)
else:
create_custom_field(doctype, df)
create_custom_field(doctype, df, ignore_validate=True)
count += 1
frappe.publish_progress(count*100/len(doclist), title = _("Creating Dimensions..."))
frappe.publish_progress(count * 100 / len(doclist), title=_("Creating Dimensions..."))
frappe.clear_cache(doctype=doctype)
def add_dimension_to_budget_doctype(df, doc):
df.update({
"insert_after": "cost_center",
"depends_on": "eval:doc.budget_against == '{0}'".format(doc.document_type)
})
create_custom_field("Budget", df)
def add_dimension_to_budget_doctype(df, doc):
df.update(
{
"insert_after": "cost_center",
"depends_on": "eval:doc.budget_against == '{0}'".format(doc.document_type),
}
)
create_custom_field("Budget", df, ignore_validate=True)
property_setter = frappe.db.exists("Property Setter", "Budget-budget_against-options")
@@ -112,36 +124,44 @@ def add_dimension_to_budget_doctype(df, doc):
property_setter_doc.value = property_setter_doc.value + "\n" + doc.document_type
property_setter_doc.save()
frappe.clear_cache(doctype='Budget')
frappe.clear_cache(doctype="Budget")
else:
frappe.get_doc({
"doctype": "Property Setter",
"doctype_or_field": "DocField",
"doc_type": "Budget",
"field_name": "budget_against",
"property": "options",
"property_type": "Text",
"value": "\nCost Center\nProject\n" + doc.document_type
}).insert(ignore_permissions=True)
frappe.get_doc(
{
"doctype": "Property Setter",
"doctype_or_field": "DocField",
"doc_type": "Budget",
"field_name": "budget_against",
"property": "options",
"property_type": "Text",
"value": "\nCost Center\nProject\n" + doc.document_type,
}
).insert(ignore_permissions=True)
def delete_accounting_dimension(doc):
doclist = get_doctypes_with_dimensions()
frappe.db.sql("""
frappe.db.sql(
"""
DELETE FROM `tabCustom Field`
WHERE fieldname = %s
AND dt IN (%s)""" % #nosec
('%s', ', '.join(['%s']* len(doclist))), tuple([doc.fieldname] + doclist))
AND dt IN (%s)"""
% ("%s", ", ".join(["%s"] * len(doclist))), # nosec
tuple([doc.fieldname] + doclist),
)
frappe.db.sql("""
frappe.db.sql(
"""
DELETE FROM `tabProperty Setter`
WHERE field_name = %s
AND doc_type IN (%s)""" % #nosec
('%s', ', '.join(['%s']* len(doclist))), tuple([doc.fieldname] + doclist))
AND doc_type IN (%s)"""
% ("%s", ", ".join(["%s"] * len(doclist))), # nosec
tuple([doc.fieldname] + doclist),
)
budget_against_property = frappe.get_doc("Property Setter", "Budget-budget_against-options")
value_list = budget_against_property.value.split('\n')[3:]
value_list = budget_against_property.value.split("\n")[3:]
if doc.document_type in value_list:
value_list.remove(doc.document_type)
@@ -152,6 +172,7 @@ def delete_accounting_dimension(doc):
for doctype in doclist:
frappe.clear_cache(doctype=doctype)
@frappe.whitelist()
def disable_dimension(doc):
if frappe.flags.in_test:
@@ -159,10 +180,11 @@ def disable_dimension(doc):
else:
frappe.enqueue(toggle_disabling, doc=doc)
def toggle_disabling(doc):
doc = json.loads(doc)
if doc.get('disabled'):
if doc.get("disabled"):
df = {"read_only": 1}
else:
df = {"read_only": 0}
@@ -170,7 +192,7 @@ def toggle_disabling(doc):
doclist = get_doctypes_with_dimensions()
for doctype in doclist:
field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": doc.get('fieldname')})
field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": doc.get("fieldname")})
if field:
custom_field = frappe.get_doc("Custom Field", field)
custom_field.update(df)
@@ -178,61 +200,82 @@ def toggle_disabling(doc):
frappe.clear_cache(doctype=doctype)
def get_doctypes_with_dimensions():
return frappe.get_hooks("accounting_dimension_doctypes")
def get_accounting_dimensions(as_list=True):
def get_accounting_dimensions(as_list=True, filters=None):
if not filters:
filters = {"disabled": 0}
if frappe.flags.accounting_dimensions is None:
frappe.flags.accounting_dimensions = frappe.get_all("Accounting Dimension",
fields=["label", "fieldname", "disabled", "document_type"])
frappe.flags.accounting_dimensions = frappe.get_all(
"Accounting Dimension",
fields=["label", "fieldname", "disabled", "document_type"],
filters=filters,
)
if as_list:
return [d.fieldname for d in frappe.flags.accounting_dimensions]
else:
return frappe.flags.accounting_dimensions
def get_checks_for_pl_and_bs_accounts():
dimensions = frappe.db.sql("""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
dimensions = frappe.db.sql(
"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
WHERE p.name = c.parent""", as_dict=1)
WHERE p.name = c.parent""",
as_dict=1,
)
return dimensions
def get_dimension_with_children(doctype, dimension):
if isinstance(dimension, list):
dimension = dimension[0]
def get_dimension_with_children(doctype, dimensions):
if isinstance(dimensions, str):
dimensions = [dimensions]
all_dimensions = []
lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"])
children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft")
all_dimensions += [c.name for c in children]
for dimension in dimensions:
lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"])
children = frappe.get_all(
doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft"
)
all_dimensions += [c.name for c in children]
return all_dimensions
@frappe.whitelist()
def get_dimensions(with_cost_center_and_project=False):
dimension_filters = frappe.db.sql("""
dimension_filters = frappe.db.sql(
"""
SELECT label, fieldname, document_type
FROM `tabAccounting Dimension`
WHERE disabled = 0
""", as_dict=1)
""",
as_dict=1,
)
default_dimensions = frappe.db.sql("""SELECT p.fieldname, c.company, c.default_dimension
default_dimensions = frappe.db.sql(
"""SELECT p.fieldname, c.company, c.default_dimension
FROM `tabAccounting Dimension Detail` c, `tabAccounting Dimension` p
WHERE c.parent = p.name""", as_dict=1)
WHERE c.parent = p.name""",
as_dict=1,
)
if with_cost_center_and_project:
dimension_filters.extend([
{
'fieldname': 'cost_center',
'document_type': 'Cost Center'
},
{
'fieldname': 'project',
'document_type': 'Project'
}
])
dimension_filters.extend(
[
{"fieldname": "cost_center", "document_type": "Cost Center"},
{"fieldname": "project", "document_type": "Project"},
]
)
default_dimensions_map = {}
for dimension in default_dimensions:

View File

@@ -8,7 +8,8 @@ import frappe
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
test_dependencies = ['Cost Center', 'Location', 'Warehouse', 'Department']
test_dependencies = ["Cost Center", "Location", "Warehouse", "Department"]
class TestAccountingDimension(unittest.TestCase):
def setUp(self):
@@ -18,24 +19,27 @@ class TestAccountingDimension(unittest.TestCase):
si = create_sales_invoice(do_not_save=1)
si.location = "Block 1"
si.append("items", {
"item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC",
"qty": 1,
"rate": 100,
"income_account": "Sales - _TC",
"expense_account": "Cost of Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
"department": "_Test Department - _TC",
"location": "Block 1"
})
si.append(
"items",
{
"item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC",
"qty": 1,
"rate": 100,
"income_account": "Sales - _TC",
"expense_account": "Cost of Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
"department": "_Test Department - _TC",
"location": "Block 1",
},
)
si.save()
si.submit()
gle = frappe.get_doc("GL Entry", {"voucher_no": si.name, "account": "Sales - _TC"})
self.assertEqual(gle.get('department'), "_Test Department - _TC")
self.assertEqual(gle.get("department"), "_Test Department - _TC")
def test_dimension_against_journal_entry(self):
je = make_journal_entry("Sales - _TC", "Sales Expenses - _TC", 500, save=False)
@@ -50,21 +54,24 @@ class TestAccountingDimension(unittest.TestCase):
gle = frappe.get_doc("GL Entry", {"voucher_no": je.name, "account": "Sales - _TC"})
gle1 = frappe.get_doc("GL Entry", {"voucher_no": je.name, "account": "Sales Expenses - _TC"})
self.assertEqual(gle.get('department'), "_Test Department - _TC")
self.assertEqual(gle1.get('department'), "_Test Department - _TC")
self.assertEqual(gle.get("department"), "_Test Department - _TC")
self.assertEqual(gle1.get("department"), "_Test Department - _TC")
def test_mandatory(self):
si = create_sales_invoice(do_not_save=1)
si.append("items", {
"item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC",
"qty": 1,
"rate": 100,
"income_account": "Sales - _TC",
"expense_account": "Cost of Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
"location": ""
})
si.append(
"items",
{
"item_code": "_Test Item",
"warehouse": "_Test Warehouse - _TC",
"qty": 1,
"rate": 100,
"income_account": "Sales - _TC",
"expense_account": "Cost of Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
"location": "",
},
)
si.save()
self.assertRaises(frappe.ValidationError, si.submit)
@@ -72,31 +79,39 @@ class TestAccountingDimension(unittest.TestCase):
def tearDown(self):
disable_dimension()
def create_dimension():
frappe.set_user("Administrator")
if not frappe.db.exists("Accounting Dimension", {"document_type": "Department"}):
frappe.get_doc({
"doctype": "Accounting Dimension",
"document_type": "Department",
}).insert()
frappe.get_doc(
{
"doctype": "Accounting Dimension",
"document_type": "Department",
}
).insert()
else:
dimension = frappe.get_doc("Accounting Dimension", "Department")
dimension.disabled = 0
dimension.save()
if not frappe.db.exists("Accounting Dimension", {"document_type": "Location"}):
dimension1 = frappe.get_doc({
"doctype": "Accounting Dimension",
"document_type": "Location",
})
dimension1 = frappe.get_doc(
{
"doctype": "Accounting Dimension",
"document_type": "Location",
}
)
dimension1.append("dimension_defaults", {
"company": "_Test Company",
"reference_document": "Location",
"default_dimension": "Block 1",
"mandatory_for_bs": 1
})
dimension1.append(
"dimension_defaults",
{
"company": "_Test Company",
"reference_document": "Location",
"default_dimension": "Block 1",
"mandatory_for_bs": 1,
},
)
dimension1.insert()
dimension1.save()
@@ -105,6 +120,7 @@ def create_dimension():
dimension1.disabled = 0
dimension1.save()
def disable_dimension():
dimension1 = frappe.get_doc("Accounting Dimension", "Department")
dimension1.disabled = 1

View File

@@ -19,17 +19,27 @@ class AccountingDimensionFilter(Document):
WHERE d.name = a.parent
and d.name != %s
and d.accounting_dimension = %s
""", (self.name, self.accounting_dimension), as_dict=1)
""",
(self.name, self.accounting_dimension),
as_dict=1,
)
account_list = [d.account for d in accounts]
for account in self.get('accounts'):
for account in self.get("accounts"):
if account.applicable_on_account in account_list:
frappe.throw(_("Row {0}: {1} account already applied for Accounting Dimension {2}").format(
account.idx, frappe.bold(account.applicable_on_account), frappe.bold(self.accounting_dimension)))
frappe.throw(
_("Row {0}: {1} account already applied for Accounting Dimension {2}").format(
account.idx,
frappe.bold(account.applicable_on_account),
frappe.bold(self.accounting_dimension),
)
)
def get_dimension_filter_map():
filters = frappe.db.sql("""
filters = frappe.db.sql(
"""
SELECT
a.applicable_on_account, d.dimension_value, p.accounting_dimension,
p.allow_or_restrict, a.is_mandatory
@@ -40,22 +50,30 @@ def get_dimension_filter_map():
p.name = a.parent
AND p.disabled = 0
AND p.name = d.parent
""", as_dict=1)
""",
as_dict=1,
)
dimension_filter_map = {}
for f in filters:
f.fieldname = scrub(f.accounting_dimension)
build_map(dimension_filter_map, f.fieldname, f.applicable_on_account, f.dimension_value,
f.allow_or_restrict, f.is_mandatory)
build_map(
dimension_filter_map,
f.fieldname,
f.applicable_on_account,
f.dimension_value,
f.allow_or_restrict,
f.is_mandatory,
)
return dimension_filter_map
def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):
map_object.setdefault((dimension, account), {
'allowed_dimensions': [],
'is_mandatory': is_mandatory,
'allow_or_restrict': allow_or_restrict
})
map_object[(dimension, account)]['allowed_dimensions'].append(filter_value)
map_object.setdefault(
(dimension, account),
{"allowed_dimensions": [], "is_mandatory": is_mandatory, "allow_or_restrict": allow_or_restrict},
)
map_object[(dimension, account)]["allowed_dimensions"].append(filter_value)

View File

@@ -12,7 +12,8 @@ from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension imp
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError
test_dependencies = ['Location', 'Cost Center', 'Department']
test_dependencies = ["Location", "Cost Center", "Department"]
class TestAccountingDimensionFilter(unittest.TestCase):
def setUp(self):
@@ -22,9 +23,9 @@ class TestAccountingDimensionFilter(unittest.TestCase):
def test_allowed_dimension_validation(self):
si = create_sales_invoice(do_not_save=1)
si.items[0].cost_center = 'Main - _TC'
si.department = 'Accounts - _TC'
si.location = 'Block 1'
si.items[0].cost_center = "Main - _TC"
si.department = "Accounts - _TC"
si.location = "Block 1"
si.save()
self.assertRaises(InvalidAccountDimensionError, si.submit)
@@ -32,12 +33,12 @@ class TestAccountingDimensionFilter(unittest.TestCase):
def test_mandatory_dimension_validation(self):
si = create_sales_invoice(do_not_save=1)
si.department = ''
si.location = 'Block 1'
si.department = ""
si.location = "Block 1"
# Test with no department for Sales Account
si.items[0].department = ''
si.items[0].cost_center = '_Test Cost Center 2 - _TC'
si.items[0].department = ""
si.items[0].cost_center = "_Test Cost Center 2 - _TC"
si.save()
self.assertRaises(MandatoryAccountDimensionError, si.submit)
@@ -52,53 +53,54 @@ class TestAccountingDimensionFilter(unittest.TestCase):
if si.docstatus == 1:
si.cancel()
def create_accounting_dimension_filter():
if not frappe.db.get_value('Accounting Dimension Filter',
{'accounting_dimension': 'Cost Center'}):
frappe.get_doc({
'doctype': 'Accounting Dimension Filter',
'accounting_dimension': 'Cost Center',
'allow_or_restrict': 'Allow',
'company': '_Test Company',
'accounts': [{
'applicable_on_account': 'Sales - _TC',
}],
'dimensions': [{
'accounting_dimension': 'Cost Center',
'dimension_value': '_Test Cost Center 2 - _TC'
}]
}).insert()
if not frappe.db.get_value(
"Accounting Dimension Filter", {"accounting_dimension": "Cost Center"}
):
frappe.get_doc(
{
"doctype": "Accounting Dimension Filter",
"accounting_dimension": "Cost Center",
"allow_or_restrict": "Allow",
"company": "_Test Company",
"accounts": [
{
"applicable_on_account": "Sales - _TC",
}
],
"dimensions": [
{"accounting_dimension": "Cost Center", "dimension_value": "_Test Cost Center 2 - _TC"}
],
}
).insert()
else:
doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Cost Center'})
doc = frappe.get_doc("Accounting Dimension Filter", {"accounting_dimension": "Cost Center"})
doc.disabled = 0
doc.save()
if not frappe.db.get_value('Accounting Dimension Filter',
{'accounting_dimension': 'Department'}):
frappe.get_doc({
'doctype': 'Accounting Dimension Filter',
'accounting_dimension': 'Department',
'allow_or_restrict': 'Allow',
'company': '_Test Company',
'accounts': [{
'applicable_on_account': 'Sales - _TC',
'is_mandatory': 1
}],
'dimensions': [{
'accounting_dimension': 'Department',
'dimension_value': 'Accounts - _TC'
}]
}).insert()
if not frappe.db.get_value("Accounting Dimension Filter", {"accounting_dimension": "Department"}):
frappe.get_doc(
{
"doctype": "Accounting Dimension Filter",
"accounting_dimension": "Department",
"allow_or_restrict": "Allow",
"company": "_Test Company",
"accounts": [{"applicable_on_account": "Sales - _TC", "is_mandatory": 1}],
"dimensions": [{"accounting_dimension": "Department", "dimension_value": "Accounts - _TC"}],
}
).insert()
else:
doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Department'})
doc = frappe.get_doc("Accounting Dimension Filter", {"accounting_dimension": "Department"})
doc.disabled = 0
doc.save()
def disable_dimension_filter():
doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Cost Center'})
doc = frappe.get_doc("Accounting Dimension Filter", {"accounting_dimension": "Cost Center"})
doc.disabled = 1
doc.save()
doc = frappe.get_doc('Accounting Dimension Filter', {'accounting_dimension': 'Department'})
doc = frappe.get_doc("Accounting Dimension Filter", {"accounting_dimension": "Department"})
doc.disabled = 1
doc.save()

View File

@@ -7,7 +7,9 @@ from frappe import _
from frappe.model.document import Document
class OverlapError(frappe.ValidationError): pass
class OverlapError(frappe.ValidationError):
pass
class AccountingPeriod(Document):
def validate(self):
@@ -17,11 +19,12 @@ class AccountingPeriod(Document):
self.bootstrap_doctypes_for_closing()
def autoname(self):
company_abbr = frappe.get_cached_value('Company', self.company, "abbr")
company_abbr = frappe.get_cached_value("Company", self.company, "abbr")
self.name = " - ".join([self.period_name, company_abbr])
def validate_overlap(self):
existing_accounting_period = frappe.db.sql("""select name from `tabAccounting Period`
existing_accounting_period = frappe.db.sql(
"""select name from `tabAccounting Period`
where (
(%(start_date)s between start_date and end_date)
or (%(end_date)s between start_date and end_date)
@@ -32,18 +35,29 @@ class AccountingPeriod(Document):
"start_date": self.start_date,
"end_date": self.end_date,
"name": self.name,
"company": self.company
}, as_dict=True)
"company": self.company,
},
as_dict=True,
)
if len(existing_accounting_period) > 0:
frappe.throw(_("Accounting Period overlaps with {0}")
.format(existing_accounting_period[0].get("name")), OverlapError)
frappe.throw(
_("Accounting Period overlaps with {0}").format(existing_accounting_period[0].get("name")),
OverlapError,
)
@frappe.whitelist()
def get_doctypes_for_closing(self):
docs_for_closing = []
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \
"Bank Clearance", "Asset", "Stock Entry"]
doctypes = [
"Sales Invoice",
"Purchase Invoice",
"Journal Entry",
"Payroll Entry",
"Bank Clearance",
"Asset",
"Stock Entry",
]
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
for closed_doctype in closed_doctypes:
docs_for_closing.append(closed_doctype)
@@ -53,7 +67,7 @@ class AccountingPeriod(Document):
def bootstrap_doctypes_for_closing(self):
if len(self.closed_documents) == 0:
for doctype_for_closing in self.get_doctypes_for_closing():
self.append('closed_documents', {
"document_type": doctype_for_closing.document_type,
"closed": doctype_for_closing.closed
})
self.append(
"closed_documents",
{"document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed},
)

View File

@@ -10,29 +10,38 @@ from erpnext.accounts.doctype.accounting_period.accounting_period import Overlap
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.general_ledger import ClosedAccountingPeriod
test_dependencies = ['Item']
test_dependencies = ["Item"]
class TestAccountingPeriod(unittest.TestCase):
def test_overlap(self):
ap1 = create_accounting_period(start_date = "2018-04-01",
end_date = "2018-06-30", company = "Wind Power LLC")
ap1 = create_accounting_period(
start_date="2018-04-01", end_date="2018-06-30", company="Wind Power LLC"
)
ap1.save()
ap2 = create_accounting_period(start_date = "2018-06-30",
end_date = "2018-07-10", company = "Wind Power LLC", period_name = "Test Accounting Period 1")
ap2 = create_accounting_period(
start_date="2018-06-30",
end_date="2018-07-10",
company="Wind Power LLC",
period_name="Test Accounting Period 1",
)
self.assertRaises(OverlapError, ap2.save)
def test_accounting_period(self):
ap1 = create_accounting_period(period_name = "Test Accounting Period 2")
ap1 = create_accounting_period(period_name="Test Accounting Period 2")
ap1.save()
doc = create_sales_invoice(do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC")
doc = create_sales_invoice(
do_not_submit=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
)
self.assertRaises(ClosedAccountingPeriod, doc.submit)
def tearDown(self):
for d in frappe.get_all("Accounting Period"):
frappe.delete_doc("Accounting Period", d.name)
def create_accounting_period(**args):
args = frappe._dict(args)
@@ -41,8 +50,6 @@ def create_accounting_period(**args):
accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
accounting_period.company = args.company or "_Test Company"
accounting_period.period_name = args.period_name or "_Test_Period_Name_1"
accounting_period.append("closed_documents", {
"document_type": 'Sales Invoice', "closed": 1
})
accounting_period.append("closed_documents", {"document_type": "Sales Invoice", "closed": 1})
return accounting_period

View File

@@ -18,11 +18,13 @@ class AccountsSettings(Document):
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))
frappe.db.set_default(
"add_taxes_from_item_tax_template", self.get("add_taxes_from_item_tax_template", 0)
)
frappe.db.set_default("enable_common_party_accounting",
self.get("enable_common_party_accounting", 0))
frappe.db.set_default(
"enable_common_party_accounting", self.get("enable_common_party_accounting", 0)
)
self.validate_stale_days()
self.enable_payment_schedule_in_print()
@@ -32,34 +34,91 @@ class AccountsSettings(Document):
def validate_stale_days(self):
if not self.allow_stale and cint(self.stale_days) <= 0:
frappe.msgprint(
_("Stale Days should start from 1."), title='Error', indicator='red',
raise_exception=1)
_("Stale Days should start from 1."), title="Error", indicator="red", raise_exception=1
)
def enable_payment_schedule_in_print(self):
show_in_print = cint(self.show_payment_schedule_in_print)
for doctype in ("Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"):
make_property_setter(doctype, "due_date", "print_hide", show_in_print, "Check", validate_fields_for_doctype=False)
make_property_setter(doctype, "payment_schedule", "print_hide", 0 if show_in_print else 1, "Check", validate_fields_for_doctype=False)
make_property_setter(
doctype, "due_date", "print_hide", show_in_print, "Check", validate_fields_for_doctype=False
)
make_property_setter(
doctype,
"payment_schedule",
"print_hide",
0 if show_in_print else 1,
"Check",
validate_fields_for_doctype=False,
)
def toggle_discount_accounting_fields(self):
enable_discount_accounting = cint(self.enable_discount_accounting)
for doctype in ["Sales Invoice Item", "Purchase Invoice Item"]:
make_property_setter(doctype, "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
make_property_setter(
doctype,
"discount_account",
"hidden",
not (enable_discount_accounting),
"Check",
validate_fields_for_doctype=False,
)
if enable_discount_accounting:
make_property_setter(doctype, "discount_account", "mandatory_depends_on", "eval: doc.discount_amount", "Code", validate_fields_for_doctype=False)
make_property_setter(
doctype,
"discount_account",
"mandatory_depends_on",
"eval: doc.discount_amount",
"Code",
validate_fields_for_doctype=False,
)
else:
make_property_setter(doctype, "discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False)
make_property_setter(
doctype,
"discount_account",
"mandatory_depends_on",
"",
"Code",
validate_fields_for_doctype=False,
)
for doctype in ["Sales Invoice", "Purchase Invoice"]:
make_property_setter(doctype, "additional_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
make_property_setter(
doctype,
"additional_discount_account",
"hidden",
not (enable_discount_accounting),
"Check",
validate_fields_for_doctype=False,
)
if enable_discount_accounting:
make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "eval: doc.discount_amount", "Code", validate_fields_for_doctype=False)
make_property_setter(
doctype,
"additional_discount_account",
"mandatory_depends_on",
"eval: doc.discount_amount",
"Code",
validate_fields_for_doctype=False,
)
else:
make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False)
make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
make_property_setter(
doctype,
"additional_discount_account",
"mandatory_depends_on",
"",
"Code",
validate_fields_for_doctype=False,
)
make_property_setter(
"Item",
"default_discount_account",
"hidden",
not (enable_discount_accounting),
"Check",
validate_fields_for_doctype=False,
)
def validate_pending_reposts(self):
if self.acc_frozen_upto:

View File

@@ -1,4 +1,3 @@
import unittest
import frappe
@@ -8,12 +7,12 @@ class TestAccountsSettings(unittest.TestCase):
def tearDown(self):
# Just in case `save` method succeeds, we need to take things back to default so that other tests
# don't break
cur_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
cur_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
cur_settings.allow_stale = 1
cur_settings.save()
def test_stale_days(self):
cur_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
cur_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
cur_settings.allow_stale = 0
cur_settings.stale_days = 0

View File

@@ -15,4 +15,4 @@ class Bank(Document):
load_address_and_contact(self)
def on_trash(self):
delete_contact_and_address('Bank', self.name)
delete_contact_and_address("Bank", self.name)

View File

@@ -1,14 +1,8 @@
from frappe import _
def get_data():
return {
'fieldname': 'bank',
'transactions': [
{
'label': _('Bank Details'),
'items': ['Bank Account', 'Bank Guarantee']
}
]
"fieldname": "bank",
"transactions": [{"label": _("Bank Details"), "items": ["Bank Account", "Bank Guarantee"]}],
}

View File

@@ -20,7 +20,7 @@ class BankAccount(Document):
self.name = self.account_name + " - " + self.bank
def on_trash(self):
delete_contact_and_address('BankAccount', self.name)
delete_contact_and_address("BankAccount", self.name)
def validate(self):
self.validate_company()
@@ -31,9 +31,9 @@ class BankAccount(Document):
frappe.throw(_("Company is manadatory for company account"))
def validate_iban(self):
'''
"""
Algorithm: https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN
'''
"""
# IBAN field is optional
if not self.iban:
return
@@ -43,7 +43,7 @@ class BankAccount(Document):
return str(9 + ord(c) - 64)
# remove whitespaces, upper case to get the right number from ord()
iban = ''.join(self.iban.split(' ')).upper()
iban = "".join(self.iban.split(" ")).upper()
# Move country code and checksum from the start to the end
flipped = iban[4:] + iban[:4]
@@ -52,12 +52,12 @@ class BankAccount(Document):
encoded = [encode_char(c) if ord(c) >= 65 and ord(c) <= 90 else c for c in flipped]
try:
to_check = int(''.join(encoded))
to_check = int("".join(encoded))
except ValueError:
frappe.throw(_('IBAN is not valid'))
frappe.throw(_("IBAN is not valid"))
if to_check % 97 != 1:
frappe.throw(_('IBAN is not valid'))
frappe.throw(_("IBAN is not valid"))
@frappe.whitelist()
@@ -69,12 +69,14 @@ def make_bank_account(doctype, docname):
return doc
@frappe.whitelist()
def get_party_bank_account(party_type, party):
return frappe.db.get_value(party_type,
party, 'default_bank_account')
return frappe.db.get_value(party_type, party, "default_bank_account")
@frappe.whitelist()
def get_bank_account_details(bank_account):
return frappe.db.get_value("Bank Account",
bank_account, ['account', 'bank', 'bank_account_no'], as_dict=1)
return frappe.db.get_value(
"Bank Account", bank_account, ["account", "bank", "bank_account_no"], as_dict=1
)

View File

@@ -1,28 +1,20 @@
from frappe import _
def get_data():
return {
'fieldname': 'bank_account',
'non_standard_fieldnames': {
'Customer': 'default_bank_account',
'Supplier': 'default_bank_account',
"fieldname": "bank_account",
"non_standard_fieldnames": {
"Customer": "default_bank_account",
"Supplier": "default_bank_account",
},
'transactions': [
"transactions": [
{
'label': _('Payments'),
'items': ['Payment Entry', 'Payment Request', 'Payment Order', 'Payroll Entry']
"label": _("Payments"),
"items": ["Payment Entry", "Payment Request", "Payment Order", "Payroll Entry"],
},
{
'label': _('Party'),
'items': ['Customer', 'Supplier']
},
{
'items': ['Bank Guarantee']
},
{
'items': ['Journal Entry']
}
]
{"label": _("Party"), "items": ["Customer", "Supplier"]},
{"items": ["Bank Guarantee"]},
{"items": ["Journal Entry"]},
],
}

View File

@@ -8,28 +8,28 @@ from frappe import ValidationError
# test_records = frappe.get_test_records('Bank Account')
class TestBankAccount(unittest.TestCase):
class TestBankAccount(unittest.TestCase):
def test_validate_iban(self):
valid_ibans = [
'GB82 WEST 1234 5698 7654 32',
'DE91 1000 0000 0123 4567 89',
'FR76 3000 6000 0112 3456 7890 189'
"GB82 WEST 1234 5698 7654 32",
"DE91 1000 0000 0123 4567 89",
"FR76 3000 6000 0112 3456 7890 189",
]
invalid_ibans = [
# wrong checksum (3rd place)
'GB72 WEST 1234 5698 7654 32',
'DE81 1000 0000 0123 4567 89',
'FR66 3000 6000 0112 3456 7890 189'
"GB72 WEST 1234 5698 7654 32",
"DE81 1000 0000 0123 4567 89",
"FR66 3000 6000 0112 3456 7890 189",
]
bank_account = frappe.get_doc({'doctype':'Bank Account'})
bank_account = frappe.get_doc({"doctype": "Bank Account"})
try:
bank_account.validate_iban()
except AttributeError:
msg = 'BankAccount.validate_iban() failed for empty IBAN'
msg = "BankAccount.validate_iban() failed for empty IBAN"
self.fail(msg=msg)
for iban in valid_ibans:
@@ -37,11 +37,11 @@ class TestBankAccount(unittest.TestCase):
try:
bank_account.validate_iban()
except ValidationError:
msg = 'BankAccount.validate_iban() failed for valid IBAN {}'.format(iban)
msg = "BankAccount.validate_iban() failed for valid IBAN {}".format(iban)
self.fail(msg=msg)
for not_iban in invalid_ibans:
bank_account.iban = not_iban
msg = 'BankAccount.validate_iban() accepted invalid IBAN {}'.format(not_iban)
msg = "BankAccount.validate_iban() accepted invalid IBAN {}".format(not_iban)
with self.assertRaises(ValidationError, msg=msg):
bank_account.validate_iban()

View File

@@ -5,11 +5,13 @@
import frappe
from frappe import _, msgprint
from frappe.model.document import Document
from frappe.utils import flt, fmt_money, getdate, nowdate
from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt, fmt_money, getdate
import erpnext
form_grid_templates = {"journal_entries": "templates/form_grid/bank_reconciliation_grid.html"}
form_grid_templates = {
"journal_entries": "templates/form_grid/bank_reconciliation_grid.html"
}
class BankClearance(Document):
@frappe.whitelist()
@@ -24,7 +26,8 @@ class BankClearance(Document):
if not self.include_reconciled_entries:
condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
journal_entries = frappe.db.sql("""
journal_entries = frappe.db.sql(
"""
select
"Journal Entry" as payment_document, t1.name as payment_entry,
t1.cheque_no as cheque_number, t1.cheque_date,
@@ -38,12 +41,18 @@ class BankClearance(Document):
and ifnull(t1.is_opening, 'No') = 'No' {condition}
group by t2.account, t1.name
order by t1.posting_date ASC, t1.name DESC
""".format(condition=condition), {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1)
""".format(
condition=condition
),
{"account": self.account, "from": self.from_date, "to": self.to_date},
as_dict=1,
)
if self.bank_account:
condition += 'and bank_account = %(bank_account)s'
condition += "and bank_account = %(bank_account)s"
payment_entries = frappe.db.sql("""
payment_entries = frappe.db.sql(
"""
select
"Payment Entry" as payment_document, name as payment_entry,
reference_no as cheque_number, reference_date as cheque_date,
@@ -58,12 +67,69 @@ class BankClearance(Document):
{condition}
order by
posting_date ASC, name DESC
""".format(condition=condition), {"account": self.account, "from":self.from_date,
"to": self.to_date, "bank_account": self.bank_account}, as_dict=1)
""".format(
condition=condition
),
{
"account": self.account,
"from": self.from_date,
"to": self.to_date,
"bank_account": self.bank_account,
},
as_dict=1,
)
loan_disbursement = frappe.qb.DocType("Loan Disbursement")
loan_disbursements = (
frappe.qb.from_(loan_disbursement)
.select(
ConstantColumn("Loan Disbursement").as_("payment_document"),
loan_disbursement.name.as_("payment_entry"),
loan_disbursement.disbursed_amount.as_("credit"),
ConstantColumn(0).as_("debit"),
loan_disbursement.reference_number.as_("cheque_number"),
loan_disbursement.reference_date.as_("cheque_date"),
loan_disbursement.disbursement_date.as_("posting_date"),
loan_disbursement.applicant.as_("against_account"),
)
.where(loan_disbursement.docstatus == 1)
.where(loan_disbursement.disbursement_date >= self.from_date)
.where(loan_disbursement.disbursement_date <= self.to_date)
.where(loan_disbursement.clearance_date.isnull())
.where(loan_disbursement.disbursement_account.isin([self.bank_account, self.account]))
.orderby(loan_disbursement.disbursement_date)
.orderby(loan_disbursement.name, frappe.qb.desc)
).run(as_dict=1)
loan_repayment = frappe.qb.DocType("Loan Repayment")
loan_repayments = (
frappe.qb.from_(loan_repayment)
.select(
ConstantColumn("Loan Repayment").as_("payment_document"),
loan_repayment.name.as_("payment_entry"),
loan_repayment.amount_paid.as_("debit"),
ConstantColumn(0).as_("credit"),
loan_repayment.reference_number.as_("cheque_number"),
loan_repayment.reference_date.as_("cheque_date"),
loan_repayment.applicant.as_("against_account"),
loan_repayment.posting_date,
)
.where(loan_repayment.docstatus == 1)
.where(loan_repayment.clearance_date.isnull())
.where(loan_repayment.repay_from_salary == 0)
.where(loan_repayment.posting_date >= self.from_date)
.where(loan_repayment.posting_date <= self.to_date)
.where(loan_repayment.payment_account.isin([self.bank_account, self.account]))
.orderby(loan_repayment.posting_date)
.orderby(loan_repayment.name, frappe.qb.desc)
).run(as_dict=1)
pos_sales_invoices, pos_purchase_invoices = [], []
if self.include_pos_transactions:
pos_sales_invoices = frappe.db.sql("""
pos_sales_invoices = frappe.db.sql(
"""
select
"Sales Invoice Payment" as payment_document, sip.name as payment_entry, sip.amount as debit,
si.posting_date, si.customer as against_account, sip.clearance_date,
@@ -74,9 +140,13 @@ class BankClearance(Document):
and account.name = sip.account and si.posting_date >= %(from)s and si.posting_date <= %(to)s
order by
si.posting_date ASC, si.name DESC
""", {"account":self.account, "from":self.from_date, "to":self.to_date}, as_dict=1)
""",
{"account": self.account, "from": self.from_date, "to": self.to_date},
as_dict=1,
)
pos_purchase_invoices = frappe.db.sql("""
pos_purchase_invoices = frappe.db.sql(
"""
select
"Purchase Invoice" as payment_document, pi.name as payment_entry, pi.paid_amount as credit,
pi.posting_date, pi.supplier as against_account, pi.clearance_date,
@@ -87,21 +157,36 @@ class BankClearance(Document):
and pi.posting_date >= %(from)s and pi.posting_date <= %(to)s
order by
pi.posting_date ASC, pi.name DESC
""", {"account": self.account, "from": self.from_date, "to": self.to_date}, as_dict=1)
""",
{"account": self.account, "from": self.from_date, "to": self.to_date},
as_dict=1,
)
entries = sorted(list(payment_entries) + list(journal_entries + list(pos_sales_invoices) + list(pos_purchase_invoices)),
key=lambda k: k['posting_date'] or getdate(nowdate()))
entries = sorted(
list(payment_entries)
+ list(journal_entries)
+ list(pos_sales_invoices)
+ list(pos_purchase_invoices)
+ list(loan_disbursements)
+ list(loan_repayments),
key=lambda k: getdate(k["posting_date"]),
)
self.set('payment_entries', [])
self.set("payment_entries", [])
self.total_amount = 0.0
default_currency = erpnext.get_default_currency()
for d in entries:
row = self.append('payment_entries', {})
row = self.append("payment_entries", {})
amount = flt(d.get('debit', 0)) - flt(d.get('credit', 0))
amount = flt(d.get("debit", 0)) - flt(d.get("credit", 0))
if not d.get("account_currency"):
d.account_currency = default_currency
formatted_amount = fmt_money(abs(amount), 2, d.account_currency)
d.amount = formatted_amount + " " + (_("Dr") if amount > 0 else _("Cr"))
d.posting_date = getdate(d.posting_date)
d.pop("credit")
d.pop("debit")
@@ -112,21 +197,24 @@ class BankClearance(Document):
@frappe.whitelist()
def update_clearance_date(self):
clearance_date_updated = False
for d in self.get('payment_entries'):
for d in self.get("payment_entries"):
if d.clearance_date:
if not d.payment_document:
frappe.throw(_("Row #{0}: Payment document is required to complete the transaction"))
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
frappe.throw(_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}")
.format(d.idx, d.clearance_date, d.cheque_date))
frappe.throw(
_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}").format(
d.idx, d.clearance_date, d.cheque_date
)
)
if d.clearance_date or self.include_reconciled_entries:
if not d.clearance_date:
d.clearance_date = None
payment_entry = frappe.get_doc(d.payment_document, d.payment_entry)
payment_entry.db_set('clearance_date', d.clearance_date)
payment_entry.db_set("clearance_date", d.clearance_date)
clearance_date_updated = True

View File

@@ -1,9 +1,96 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
# import frappe
import unittest
import frappe
from frappe.utils import add_months, getdate
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.loan_management.doctype.loan.test_loan import (
create_loan,
create_loan_accounts,
create_loan_type,
create_repayment_entry,
make_loan_disbursement_entry,
)
class TestBankClearance(unittest.TestCase):
pass
@classmethod
def setUpClass(cls):
make_bank_account()
create_loan_accounts()
create_loan_masters()
add_transactions()
# Basic test case to test if bank clearance tool doesn't break
# Detailed test can be added later
def test_bank_clearance(self):
bank_clearance = frappe.get_doc("Bank Clearance")
bank_clearance.account = "_Test Bank Clearance - _TC"
bank_clearance.from_date = add_months(getdate(), -1)
bank_clearance.to_date = getdate()
bank_clearance.get_payment_entries()
self.assertEqual(len(bank_clearance.payment_entries), 3)
def make_bank_account():
if not frappe.db.get_value("Account", "_Test Bank Clearance - _TC"):
frappe.get_doc(
{
"doctype": "Account",
"account_type": "Bank",
"account_name": "_Test Bank Clearance",
"company": "_Test Company",
"parent_account": "Bank Accounts - _TC",
}
).insert()
def create_loan_masters():
create_loan_type(
"Clearance Loan",
2000000,
13.5,
25,
0,
5,
"Cash",
"_Test Bank Clearance - _TC",
"_Test Bank Clearance - _TC",
"Loan Account - _TC",
"Interest Income Account - _TC",
"Penalty Income Account - _TC",
)
def add_transactions():
make_payment_entry()
make_loan()
def make_loan():
loan = create_loan(
"_Test Customer",
"Clearance Loan",
280000,
"Repay Over Number of Periods",
20,
applicant_type="Customer",
)
loan.submit()
make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=getdate())
repayment_entry = create_repayment_entry(loan.name, "_Test Customer", getdate(), loan.loan_amount)
repayment_entry.save()
repayment_entry.submit()
def make_payment_entry():
pi = make_purchase_invoice(supplier="_Test Supplier", qty=1, rate=690)
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank Clearance - _TC")
pe.reference_no = "Conrad Oct 18"
pe.reference_date = "2018-10-24"
pe.insert()
pe.submit()

View File

@@ -23,10 +23,16 @@ class BankGuarantee(Document):
if not self.bank:
frappe.throw(_("Enter the name of the bank or lending institution before submittting."))
@frappe.whitelist()
def get_vouchar_detials(column_list, doctype, docname):
column_list = json.loads(column_list)
for col in column_list:
sanitize_searchfield(col)
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
.format(columns=", ".join(column_list), doctype=doctype), docname, as_dict=1)[0]
return frappe.db.sql(
""" select {columns} from `tab{doctype}` where name=%s""".format(
columns=", ".join(column_list), doctype=doctype
),
docname,
as_dict=1,
)[0]

View File

@@ -7,6 +7,7 @@ import json
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt
from erpnext import get_company_currency
@@ -21,48 +22,63 @@ from erpnext.accounts.utils import get_balance_on
class BankReconciliationTool(Document):
pass
@frappe.whitelist()
def get_bank_transactions(bank_account, from_date = None, to_date = None):
def get_bank_transactions(bank_account, from_date=None, to_date=None):
# returns bank transactions for a bank account
filters = []
filters.append(['bank_account', '=', bank_account])
filters.append(['docstatus', '=', 1])
filters.append(['unallocated_amount', '>', 0])
filters.append(["bank_account", "=", bank_account])
filters.append(["docstatus", "=", 1])
filters.append(["unallocated_amount", ">", 0])
if to_date:
filters.append(['date', '<=', to_date])
filters.append(["date", "<=", to_date])
if from_date:
filters.append(['date', '>=', from_date])
filters.append(["date", ">=", from_date])
transactions = frappe.get_all(
'Bank Transaction',
fields = ['date', 'deposit', 'withdrawal', 'currency',
'description', 'name', 'bank_account', 'company',
'unallocated_amount', 'reference_number', 'party_type', 'party'],
filters = filters
"Bank Transaction",
fields=[
"date",
"deposit",
"withdrawal",
"currency",
"description",
"name",
"bank_account",
"company",
"unallocated_amount",
"reference_number",
"party_type",
"party",
],
filters=filters,
)
return transactions
@frappe.whitelist()
def get_account_balance(bank_account, till_date):
# returns account balance till the specified date
account = frappe.db.get_value('Bank Account', bank_account, 'account')
filters = frappe._dict({
"account": account,
"report_date": till_date,
"include_pos_transactions": 1
})
account = frappe.db.get_value("Bank Account", bank_account, "account")
filters = frappe._dict(
{"account": account, "report_date": till_date, "include_pos_transactions": 1}
)
data = get_entries(filters)
balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
total_debit, total_credit = 0,0
total_debit, total_credit = 0, 0
for d in data:
total_debit += flt(d.debit)
total_credit += flt(d.credit)
amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
bank_bal = flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) \
bank_bal = (
flt(balance_as_per_system)
- flt(total_debit)
+ flt(total_credit)
+ amounts_not_reflected_in_system
)
return bank_bal
@@ -75,71 +91,94 @@ def update_bank_transaction(bank_transaction_name, reference_number, party_type=
bank_transaction.party_type = party_type
bank_transaction.party = party
bank_transaction.save()
return frappe.db.get_all('Bank Transaction',
filters={
'name': bank_transaction_name
},
fields=['date', 'deposit', 'withdrawal', 'currency',
'description', 'name', 'bank_account', 'company',
'unallocated_amount', 'reference_number',
'party_type', 'party'],
return frappe.db.get_all(
"Bank Transaction",
filters={"name": bank_transaction_name},
fields=[
"date",
"deposit",
"withdrawal",
"currency",
"description",
"name",
"bank_account",
"company",
"unallocated_amount",
"reference_number",
"party_type",
"party",
],
)[0]
@frappe.whitelist()
def create_journal_entry_bts( bank_transaction_name, reference_number=None, reference_date=None, posting_date=None, entry_type=None,
second_account=None, mode_of_payment=None, party_type=None, party=None, allow_edit=None):
def create_journal_entry_bts(
bank_transaction_name,
reference_number=None,
reference_date=None,
posting_date=None,
entry_type=None,
second_account=None,
mode_of_payment=None,
party_type=None,
party=None,
allow_edit=None,
):
# Create a new journal entry based on the bank transaction
bank_transaction = frappe.db.get_values(
"Bank Transaction", bank_transaction_name,
fieldname=["name", "deposit", "withdrawal", "bank_account"] ,
as_dict=True
"Bank Transaction",
bank_transaction_name,
fieldname=["name", "deposit", "withdrawal", "bank_account"],
as_dict=True,
)[0]
company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
account_type = frappe.db.get_value("Account", second_account, "account_type")
if account_type in ["Receivable", "Payable"]:
if not (party_type and party):
frappe.throw(_("Party Type and Party is required for Receivable / Payable account {0}").format( second_account))
frappe.throw(
_("Party Type and Party is required for Receivable / Payable account {0}").format(
second_account
)
)
accounts = []
# Multi Currency?
accounts.append({
accounts.append(
{
"account": second_account,
"credit_in_account_currency": bank_transaction.deposit
if bank_transaction.deposit > 0
else 0,
"debit_in_account_currency":bank_transaction.withdrawal
if bank_transaction.withdrawal > 0
else 0,
"party_type":party_type,
"party":party,
})
"credit_in_account_currency": bank_transaction.deposit if bank_transaction.deposit > 0 else 0,
"debit_in_account_currency": bank_transaction.withdrawal
if bank_transaction.withdrawal > 0
else 0,
"party_type": party_type,
"party": party,
}
)
accounts.append({
accounts.append(
{
"account": company_account,
"bank_account": bank_transaction.bank_account,
"credit_in_account_currency": bank_transaction.withdrawal
if bank_transaction.withdrawal > 0
else 0,
"debit_in_account_currency":bank_transaction.deposit
if bank_transaction.deposit > 0
else 0,
})
if bank_transaction.withdrawal > 0
else 0,
"debit_in_account_currency": bank_transaction.deposit if bank_transaction.deposit > 0 else 0,
}
)
company = frappe.get_value("Account", company_account, "company")
journal_entry_dict = {
"voucher_type" : entry_type,
"company" : company,
"posting_date" : posting_date,
"cheque_date" : reference_date,
"cheque_no" : reference_number,
"mode_of_payment" : mode_of_payment
"voucher_type": entry_type,
"company": company,
"posting_date": posting_date,
"cheque_date": reference_date,
"cheque_no": reference_number,
"mode_of_payment": mode_of_payment,
}
journal_entry = frappe.new_doc('Journal Entry')
journal_entry = frappe.new_doc("Journal Entry")
journal_entry.update(journal_entry_dict)
journal_entry.set("accounts", accounts)
if allow_edit:
return journal_entry
@@ -151,21 +190,32 @@ def create_journal_entry_bts( bank_transaction_name, reference_number=None, refe
else:
paid_amount = bank_transaction.withdrawal
vouchers = json.dumps([{
"payment_doctype":"Journal Entry",
"payment_name":journal_entry.name,
"amount":paid_amount}])
vouchers = json.dumps(
[{"payment_doctype": "Journal Entry", "payment_name": journal_entry.name, "amount": paid_amount}]
)
return reconcile_vouchers(bank_transaction.name, vouchers)
@frappe.whitelist()
def create_payment_entry_bts( bank_transaction_name, reference_number=None, reference_date=None, party_type=None, party=None, posting_date=None,
mode_of_payment=None, project=None, cost_center=None, allow_edit=None):
def create_payment_entry_bts(
bank_transaction_name,
reference_number=None,
reference_date=None,
party_type=None,
party=None,
posting_date=None,
mode_of_payment=None,
project=None,
cost_center=None,
allow_edit=None,
):
# Create a new payment entry based on the bank transaction
bank_transaction = frappe.db.get_values(
"Bank Transaction", bank_transaction_name,
fieldname=["name", "unallocated_amount", "deposit", "bank_account"] ,
as_dict=True
"Bank Transaction",
bank_transaction_name,
fieldname=["name", "unallocated_amount", "deposit", "bank_account"],
as_dict=True,
)[0]
paid_amount = bank_transaction.unallocated_amount
payment_type = "Receive" if bank_transaction.deposit > 0 else "Pay"
@@ -173,27 +223,26 @@ def create_payment_entry_bts( bank_transaction_name, reference_number=None, refe
company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
company = frappe.get_value("Account", company_account, "company")
payment_entry_dict = {
"company" : company,
"payment_type" : payment_type,
"reference_no" : reference_number,
"reference_date" : reference_date,
"party_type" : party_type,
"party" : party,
"posting_date" : posting_date,
"company": company,
"payment_type": payment_type,
"reference_no": reference_number,
"reference_date": reference_date,
"party_type": party_type,
"party": party,
"posting_date": posting_date,
"paid_amount": paid_amount,
"received_amount": paid_amount
"received_amount": paid_amount,
}
payment_entry = frappe.new_doc("Payment Entry")
payment_entry.update(payment_entry_dict)
if mode_of_payment:
payment_entry.mode_of_payment = mode_of_payment
payment_entry.mode_of_payment = mode_of_payment
if project:
payment_entry.project = project
payment_entry.project = project
if cost_center:
payment_entry.cost_center = cost_center
payment_entry.cost_center = cost_center
if payment_type == "Receive":
payment_entry.paid_to = company_account
else:
@@ -207,80 +256,111 @@ def create_payment_entry_bts( bank_transaction_name, reference_number=None, refe
payment_entry.insert()
payment_entry.submit()
vouchers = json.dumps([{
"payment_doctype":"Payment Entry",
"payment_name":payment_entry.name,
"amount":paid_amount}])
vouchers = json.dumps(
[{"payment_doctype": "Payment Entry", "payment_name": payment_entry.name, "amount": paid_amount}]
)
return reconcile_vouchers(bank_transaction.name, vouchers)
@frappe.whitelist()
def reconcile_vouchers(bank_transaction_name, vouchers):
# updated clear date of all the vouchers based on the bank transaction
vouchers = json.loads(vouchers)
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
company_account = frappe.db.get_value('Bank Account', transaction.bank_account, 'account')
company_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
if transaction.unallocated_amount == 0:
frappe.throw(_("This bank transaction is already fully reconciled"))
total_amount = 0
for voucher in vouchers:
voucher['payment_entry'] = frappe.get_doc(voucher['payment_doctype'], voucher['payment_name'])
total_amount += get_paid_amount(frappe._dict({
'payment_document': voucher['payment_doctype'],
'payment_entry': voucher['payment_name'],
}), transaction.currency, company_account)
voucher["payment_entry"] = frappe.get_doc(voucher["payment_doctype"], voucher["payment_name"])
total_amount += get_paid_amount(
frappe._dict(
{
"payment_document": voucher["payment_doctype"],
"payment_entry": voucher["payment_name"],
}
),
transaction.currency,
company_account,
)
if total_amount > transaction.unallocated_amount:
frappe.throw(_("The Sum Total of Amounts of All Selected Vouchers Should be Less than the Unallocated Amount of the Bank Transaction"))
frappe.throw(
_(
"The sum total of amounts of all selected vouchers should be less than the unallocated amount of the bank transaction"
)
)
account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
for voucher in vouchers:
gl_entry = frappe.db.get_value("GL Entry", dict(account=account, voucher_type=voucher['payment_doctype'], voucher_no=voucher['payment_name']), ['credit', 'debit'], as_dict=1)
gl_amount, transaction_amount = (gl_entry.credit, transaction.deposit) if gl_entry.credit > 0 else (gl_entry.debit, transaction.withdrawal)
gl_entry = frappe.db.get_value(
"GL Entry",
dict(
account=account, voucher_type=voucher["payment_doctype"], voucher_no=voucher["payment_name"]
),
["credit", "debit"],
as_dict=1,
)
gl_amount, transaction_amount = (
(gl_entry.credit, transaction.deposit)
if gl_entry.credit > 0
else (gl_entry.debit, transaction.withdrawal)
)
allocated_amount = gl_amount if gl_amount >= transaction_amount else transaction_amount
transaction.append("payment_entries", {
"payment_document": voucher['payment_entry'].doctype,
"payment_entry": voucher['payment_entry'].name,
"allocated_amount": allocated_amount
})
transaction.append(
"payment_entries",
{
"payment_document": voucher["payment_entry"].doctype,
"payment_entry": voucher["payment_entry"].name,
"allocated_amount": allocated_amount,
},
)
transaction.save()
transaction.update_allocations()
return frappe.get_doc("Bank Transaction", bank_transaction_name)
@frappe.whitelist()
def get_linked_payments(bank_transaction_name, document_types = None):
def get_linked_payments(bank_transaction_name, document_types=None):
# get all matching payments for a bank transaction
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
bank_account = frappe.db.get_values(
"Bank Account",
transaction.bank_account,
["account", "company"],
as_dict=True)[0]
"Bank Account", transaction.bank_account, ["account", "company"], as_dict=True
)[0]
(account, company) = (bank_account.account, bank_account.company)
matching = check_matching(account, company, transaction, document_types)
return matching
def check_matching(bank_account, company, transaction, document_types):
# combine all types of vouchers
subquery = get_queries(bank_account, company, transaction, document_types)
filters = {
"amount": transaction.unallocated_amount,
"payment_type" : "Receive" if transaction.deposit > 0 else "Pay",
"reference_no": transaction.reference_number,
"party_type": transaction.party_type,
"party": transaction.party,
"bank_account": bank_account
}
"amount": transaction.unallocated_amount,
"payment_type": "Receive" if transaction.deposit > 0 else "Pay",
"reference_no": transaction.reference_number,
"party_type": transaction.party_type,
"party": transaction.party,
"bank_account": bank_account,
}
matching_vouchers = []
matching_vouchers.extend(get_loan_vouchers(bank_account, transaction, document_types, filters))
for query in subquery:
matching_vouchers.extend(
frappe.db.sql(query, filters,)
frappe.db.sql(
query,
filters,
)
)
return sorted(matching_vouchers, key = lambda x: x[0], reverse=True) if matching_vouchers else []
return sorted(matching_vouchers, key=lambda x: x[0], reverse=True) if matching_vouchers else []
def get_queries(bank_account, company, transaction, document_types):
# get queries to get matching vouchers
@@ -297,7 +377,7 @@ def get_queries(bank_account, company, transaction, document_types):
queries.extend([je_amount_matching])
if transaction.deposit > 0 and "sales_invoice" in document_types:
si_amount_matching = get_si_matching_query(amount_condition)
si_amount_matching = get_si_matching_query(amount_condition)
queries.extend([si_amount_matching])
if transaction.withdrawal > 0:
@@ -311,13 +391,104 @@ def get_queries(bank_account, company, transaction, document_types):
return queries
def get_loan_vouchers(bank_account, transaction, document_types, filters):
vouchers = []
amount_condition = True if "exact_match" in document_types else False
if transaction.withdrawal > 0 and "loan_disbursement" in document_types:
vouchers.extend(get_ld_matching_query(bank_account, amount_condition, filters))
if transaction.deposit > 0 and "loan_repayment" in document_types:
vouchers.extend(get_lr_matching_query(bank_account, amount_condition, filters))
return vouchers
def get_ld_matching_query(bank_account, amount_condition, filters):
loan_disbursement = frappe.qb.DocType("Loan Disbursement")
matching_reference = loan_disbursement.reference_number == filters.get("reference_number")
matching_party = loan_disbursement.applicant_type == filters.get(
"party_type"
) and loan_disbursement.applicant == filters.get("party")
rank = frappe.qb.terms.Case().when(matching_reference, 1).else_(0)
rank1 = frappe.qb.terms.Case().when(matching_party, 1).else_(0)
query = (
frappe.qb.from_(loan_disbursement)
.select(
rank + rank1 + 1,
ConstantColumn("Loan Disbursement").as_("doctype"),
loan_disbursement.name,
loan_disbursement.disbursed_amount,
loan_disbursement.reference_number,
loan_disbursement.reference_date,
loan_disbursement.applicant_type,
loan_disbursement.disbursement_date,
)
.where(loan_disbursement.docstatus == 1)
.where(loan_disbursement.clearance_date.isnull())
.where(loan_disbursement.disbursement_account == bank_account)
)
if amount_condition:
query.where(loan_disbursement.disbursed_amount == filters.get("amount"))
else:
query.where(loan_disbursement.disbursed_amount <= filters.get("amount"))
vouchers = query.run(as_list=True)
return vouchers
def get_lr_matching_query(bank_account, amount_condition, filters):
loan_repayment = frappe.qb.DocType("Loan Repayment")
matching_reference = loan_repayment.reference_number == filters.get("reference_number")
matching_party = loan_repayment.applicant_type == filters.get(
"party_type"
) and loan_repayment.applicant == filters.get("party")
rank = frappe.qb.terms.Case().when(matching_reference, 1).else_(0)
rank1 = frappe.qb.terms.Case().when(matching_party, 1).else_(0)
query = (
frappe.qb.from_(loan_repayment)
.select(
rank + rank1 + 1,
ConstantColumn("Loan Repayment").as_("doctype"),
loan_repayment.name,
loan_repayment.amount_paid,
loan_repayment.reference_number,
loan_repayment.reference_date,
loan_repayment.applicant_type,
loan_repayment.posting_date,
)
.where(loan_repayment.docstatus == 1)
.where(loan_repayment.repay_from_salary == 0)
.where(loan_repayment.clearance_date.isnull())
.where(loan_repayment.payment_account == bank_account)
)
if amount_condition:
query.where(loan_repayment.amount_paid == filters.get("amount"))
else:
query.where(loan_repayment.amount_paid <= filters.get("amount"))
vouchers = query.run()
return vouchers
def get_pe_matching_query(amount_condition, account_from_to, transaction):
# get matching payment entries query
if transaction.deposit > 0:
currency_field = "paid_to_account_currency as currency"
else:
currency_field = "paid_from_account_currency as currency"
return f"""
return f"""
SELECT
(CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END
+ CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END
@@ -348,7 +519,6 @@ def get_je_matching_query(amount_condition, transaction):
# We have mapping at the bank level
# So one bank could have both types of bank accounts like asset and liability
# So cr_or_dr should be judged only on basis of withdrawal and deposit and not account type
company_account = frappe.get_value("Bank Account", transaction.bank_account, "account")
cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
return f"""
@@ -407,6 +577,7 @@ def get_si_matching_query(amount_condition):
AND si.docstatus = 1
"""
def get_pi_matching_query(amount_condition):
# get matching purchase invoice query
return f"""
@@ -432,11 +603,16 @@ def get_pi_matching_query(amount_condition):
AND cash_bank_account = %(bank_account)s
"""
def get_ec_matching_query(bank_account, company, amount_condition):
# get matching Expense Claim query
mode_of_payments = [x["parent"] for x in frappe.db.get_all("Mode of Payment Account",
filters={"default_account": bank_account}, fields=["parent"])]
mode_of_payments = '(\'' + '\', \''.join(mode_of_payments) + '\' )'
mode_of_payments = [
x["parent"]
for x in frappe.db.get_all(
"Mode of Payment Account", filters={"default_account": bank_account}, fields=["parent"]
)
]
mode_of_payments = "('" + "', '".join(mode_of_payments) + "' )"
company_currency = get_company_currency(company)
return f"""
SELECT

View File

@@ -19,6 +19,7 @@ from six import string_types
INVALID_VALUES = ("", None)
class BankStatementImport(DataImport):
def __init__(self, *args, **kwargs):
super(BankStatementImport, self).__init__(*args, **kwargs)
@@ -50,16 +51,14 @@ class BankStatementImport(DataImport):
self.import_file, self.google_sheets_url
)
if 'Bank Account' not in json.dumps(preview['columns']):
if "Bank Account" not in json.dumps(preview["columns"]):
frappe.throw(_("Please add the Bank Account column"))
from frappe.core.page.background_jobs.background_jobs import get_info
from frappe.utils.scheduler import is_scheduler_inactive
if is_scheduler_inactive() and not frappe.flags.in_test:
frappe.throw(
_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive")
)
frappe.throw(_("Scheduler is inactive. Cannot import data."), title=_("Scheduler Inactive"))
enqueued_jobs = [d.get("job_name") for d in get_info()]
@@ -82,21 +81,25 @@ class BankStatementImport(DataImport):
return False
@frappe.whitelist()
def get_preview_from_template(data_import, import_file=None, google_sheets_url=None):
return frappe.get_doc("Bank Statement Import", data_import).get_preview_from_template(
import_file, google_sheets_url
)
@frappe.whitelist()
def form_start_import(data_import):
return frappe.get_doc("Bank Statement Import", data_import).start_import()
@frappe.whitelist()
def download_errored_template(data_import_name):
data_import = frappe.get_doc("Bank Statement Import", data_import_name)
data_import.export_errored_rows()
def parse_data_from_template(raw_data):
data = []
@@ -109,7 +112,10 @@ def parse_data_from_template(raw_data):
return data
def start_import(data_import, bank_account, import_file_path, google_sheets_url, bank, template_options):
def start_import(
data_import, bank_account, import_file_path, google_sheets_url, bank, template_options
):
"""This method runs in background job"""
update_mapping_db(bank, template_options)
@@ -117,7 +123,7 @@ def start_import(data_import, bank_account, import_file_path, google_sheets_url,
data_import = frappe.get_doc("Bank Statement Import", data_import)
file = import_file_path if import_file_path else google_sheets_url
import_file = ImportFile("Bank Transaction", file = file, import_type="Insert New Records")
import_file = ImportFile("Bank Transaction", file=file, import_type="Insert New Records")
data = parse_data_from_template(import_file.raw_data)
@@ -137,16 +143,18 @@ def start_import(data_import, bank_account, import_file_path, google_sheets_url,
frappe.publish_realtime("data_import_refresh", {"data_import": data_import.name})
def update_mapping_db(bank, template_options):
bank = frappe.get_doc("Bank", bank)
for d in bank.bank_transaction_mapping:
d.delete()
for d in json.loads(template_options)["column_to_field_map"].items():
bank.append("bank_transaction_mapping", {"bank_transaction_field": d[1] ,"file_field": d[0]} )
bank.append("bank_transaction_mapping", {"bank_transaction_field": d[1], "file_field": d[0]})
bank.save()
def add_bank_account(data, bank_account):
bank_account_loc = None
if "Bank Account" not in data[0]:
@@ -162,6 +170,7 @@ def add_bank_account(data, bank_account):
else:
row.append(bank_account)
def write_files(import_file, data):
full_file_path = import_file.file_doc.get_full_path()
parts = import_file.file_doc.get_extension()
@@ -169,11 +178,12 @@ def write_files(import_file, data):
extension = extension.lstrip(".")
if extension == "csv":
with open(full_file_path, 'w', newline='') as file:
with open(full_file_path, "w", newline="") as file:
writer = csv.writer(file)
writer.writerows(data)
elif extension == "xlsx" or "xls":
write_xlsx(data, "trans", file_path = full_file_path)
write_xlsx(data, "trans", file_path=full_file_path)
def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None):
# from xlsx utils with changes
@@ -188,19 +198,21 @@ def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None):
ws.column_dimensions[get_column_letter(i + 1)].width = column_width
row1 = ws.row_dimensions[1]
row1.font = Font(name='Calibri', bold=True)
row1.font = Font(name="Calibri", bold=True)
for row in data:
clean_row = []
for item in row:
if isinstance(item, string_types) and (sheet_name not in ['Data Import Template', 'Data Export']):
if isinstance(item, string_types) and (
sheet_name not in ["Data Import Template", "Data Export"]
):
value = handle_html(item)
else:
value = item
if isinstance(item, string_types) and next(ILLEGAL_CHARACTERS_RE.finditer(value), None):
# Remove illegal characters from the string
value = re.sub(ILLEGAL_CHARACTERS_RE, '', value)
value = re.sub(ILLEGAL_CHARACTERS_RE, "", value)
clean_row.append(value)
@@ -209,19 +221,20 @@ def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None):
wb.save(file_path)
return True
@frappe.whitelist()
def upload_bank_statement(**args):
args = frappe._dict(args)
bsi = frappe.new_doc("Bank Statement Import")
if args.company:
bsi.update({
"company": args.company,
})
bsi.update(
{
"company": args.company,
}
)
if args.bank_account:
bsi.update({
"bank_account": args.bank_account
})
bsi.update({"bank_account": args.bank_account})
return bsi

View File

@@ -28,17 +28,26 @@ class BankTransaction(StatusUpdater):
def update_allocations(self):
if self.payment_entries:
allocated_amount = reduce(lambda x, y: flt(x) + flt(y), [x.allocated_amount for x in self.payment_entries])
allocated_amount = reduce(
lambda x, y: flt(x) + flt(y), [x.allocated_amount for x in self.payment_entries]
)
else:
allocated_amount = 0
if allocated_amount:
frappe.db.set_value(self.doctype, self.name, "allocated_amount", flt(allocated_amount))
frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit)) - flt(allocated_amount))
frappe.db.set_value(
self.doctype,
self.name,
"unallocated_amount",
abs(flt(self.withdrawal) - flt(self.deposit)) - flt(allocated_amount),
)
else:
frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0)
frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit)))
frappe.db.set_value(
self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit))
)
amount = self.deposit or self.withdrawal
if amount == self.allocated_amount:
@@ -48,7 +57,14 @@ class BankTransaction(StatusUpdater):
def clear_linked_payment_entries(self, for_cancel=False):
for payment_entry in self.payment_entries:
if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
if payment_entry.payment_document in [
"Payment Entry",
"Journal Entry",
"Purchase Invoice",
"Expense Claim",
"Loan Repayment",
"Loan Disbursement",
]:
self.clear_simple_entry(payment_entry, for_cancel=for_cancel)
elif payment_entry.payment_document == "Sales Invoice":
@@ -56,38 +72,41 @@ class BankTransaction(StatusUpdater):
def clear_simple_entry(self, payment_entry, for_cancel=False):
if payment_entry.payment_document == "Payment Entry":
if frappe.db.get_value("Payment Entry", payment_entry.payment_entry, "payment_type") == "Internal Transfer":
if (
frappe.db.get_value("Payment Entry", payment_entry.payment_entry, "payment_type")
== "Internal Transfer"
):
if len(get_reconciled_bank_transactions(payment_entry)) < 2:
return
clearance_date = self.date if not for_cancel else None
frappe.db.set_value(
payment_entry.payment_document, payment_entry.payment_entry,
"clearance_date", clearance_date)
payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", clearance_date
)
def clear_sales_invoice(self, payment_entry, for_cancel=False):
clearance_date = self.date if not for_cancel else None
frappe.db.set_value(
"Sales Invoice Payment",
dict(
parenttype=payment_entry.payment_document,
parent=payment_entry.payment_entry
),
"clearance_date", clearance_date)
dict(parenttype=payment_entry.payment_document, parent=payment_entry.payment_entry),
"clearance_date",
clearance_date,
)
def get_reconciled_bank_transactions(payment_entry):
reconciled_bank_transactions = frappe.get_all(
'Bank Transaction Payments',
filters = {
'payment_entry': payment_entry.payment_entry
},
fields = ['parent']
"Bank Transaction Payments",
filters={"payment_entry": payment_entry.payment_entry},
fields=["parent"],
)
return reconciled_bank_transactions
def get_total_allocated_amount(payment_entry):
return frappe.db.sql("""
return frappe.db.sql(
"""
SELECT
SUM(btp.allocated_amount) as allocated_amount,
bt.name
@@ -100,36 +119,73 @@ def get_total_allocated_amount(payment_entry):
AND
btp.payment_entry = %s
AND
bt.docstatus = 1""", (payment_entry.payment_document, payment_entry.payment_entry), as_dict=True)
bt.docstatus = 1""",
(payment_entry.payment_document, payment_entry.payment_entry),
as_dict=True,
)
def get_paid_amount(payment_entry, currency, bank_account):
if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
paid_amount_field = "paid_amount"
if payment_entry.payment_document == 'Payment Entry':
if payment_entry.payment_document == "Payment Entry":
doc = frappe.get_doc("Payment Entry", payment_entry.payment_entry)
paid_amount_field = ("base_paid_amount"
if doc.paid_to_account_currency == currency else "paid_amount")
return frappe.db.get_value(payment_entry.payment_document,
payment_entry.payment_entry, paid_amount_field)
if doc.payment_type == "Receive":
paid_amount_field = (
"received_amount" if doc.paid_to_account_currency == currency else "base_received_amount"
)
elif doc.payment_type == "Pay":
paid_amount_field = (
"paid_amount" if doc.paid_to_account_currency == currency else "base_paid_amount"
)
return frappe.db.get_value(
payment_entry.payment_document, payment_entry.payment_entry, paid_amount_field
)
elif payment_entry.payment_document == "Journal Entry":
return frappe.db.get_value('Journal Entry Account', {'parent': payment_entry.payment_entry, 'account': bank_account}, "sum(credit_in_account_currency)")
return frappe.db.get_value(
"Journal Entry Account",
{"parent": payment_entry.payment_entry, "account": bank_account},
"sum(credit_in_account_currency)",
)
elif payment_entry.payment_document == "Expense Claim":
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed")
return frappe.db.get_value(
payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed"
)
elif payment_entry.payment_document == "Loan Disbursement":
return frappe.db.get_value(
payment_entry.payment_document, payment_entry.payment_entry, "disbursed_amount"
)
elif payment_entry.payment_document == "Loan Repayment":
return frappe.db.get_value(
payment_entry.payment_document, payment_entry.payment_entry, "amount_paid"
)
else:
frappe.throw("Please reconcile {0}: {1} manually".format(payment_entry.payment_document, payment_entry.payment_entry))
frappe.throw(
"Please reconcile {0}: {1} manually".format(
payment_entry.payment_document, payment_entry.payment_entry
)
)
@frappe.whitelist()
def unclear_reference_payment(doctype, docname):
if frappe.db.exists(doctype, docname):
doc = frappe.get_doc(doctype, docname)
if doctype == "Sales Invoice":
frappe.db.set_value("Sales Invoice Payment", dict(parenttype=doc.payment_document,
parent=doc.payment_entry), "clearance_date", None)
frappe.db.set_value(
"Sales Invoice Payment",
dict(parenttype=doc.payment_document, parent=doc.payment_entry),
"clearance_date",
None,
)
else:
frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)

View File

@@ -19,12 +19,14 @@ def upload_bank_statement():
fcontent = frappe.local.uploaded_file
fname = frappe.local.uploaded_filename
if frappe.safe_encode(fname).lower().endswith("csv".encode('utf-8')):
if frappe.safe_encode(fname).lower().endswith("csv".encode("utf-8")):
from frappe.utils.csvutils import read_csv_content
rows = read_csv_content(fcontent, False)
elif frappe.safe_encode(fname).lower().endswith("xlsx".encode('utf-8')):
elif frappe.safe_encode(fname).lower().endswith("xlsx".encode("utf-8")):
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
rows = read_xlsx_file_from_attached_file(fcontent=fcontent)
columns = rows[0]
@@ -44,12 +46,10 @@ def create_bank_entries(columns, data, bank_account):
continue
fields = {}
for key, value in iteritems(header_map):
fields.update({key: d[int(value)-1]})
fields.update({key: d[int(value) - 1]})
try:
bank_transaction = frappe.get_doc({
"doctype": "Bank Transaction"
})
bank_transaction = frappe.get_doc({"doctype": "Bank Transaction"})
bank_transaction.update(fields)
bank_transaction.date = getdate(parse_date(bank_transaction.date))
bank_transaction.bank_account = bank_account
@@ -62,6 +62,7 @@ def create_bank_entries(columns, data, bank_account):
return {"success": success, "errors": errors}
def get_header_mapping(columns, bank_account):
mapping = get_bank_mapping(bank_account)
@@ -72,10 +73,11 @@ def get_header_mapping(columns, bank_account):
return header_map
def get_bank_mapping(bank_account):
bank_name = frappe.db.get_value("Bank Account", bank_account, "bank")
bank = frappe.get_doc("Bank", bank_name)
mapping = {row.file_field:row.bank_transaction_field for row in bank.bank_transaction_mapping}
mapping = {row.file_field: row.bank_transaction_field for row in bank.bank_transaction_mapping}
return mapping

View File

@@ -17,6 +17,7 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal
test_dependencies = ["Item", "Cost Center"]
class TestBankTransaction(unittest.TestCase):
@classmethod
def setUpClass(cls):
@@ -41,21 +42,34 @@ class TestBankTransaction(unittest.TestCase):
# This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction.
def test_linked_payments(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"))
linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
bank_transaction = frappe.get_doc(
"Bank Transaction",
dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"),
)
linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"])
self.assertTrue(linked_payments[0][6] == "Conrad Electronic")
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
def test_reconcile(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"))
bank_transaction = frappe.get_doc(
"Bank Transaction",
dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"),
)
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1700))
vouchers = json.dumps([{
"payment_doctype":"Payment Entry",
"payment_name":payment.name,
"amount":bank_transaction.unallocated_amount}])
vouchers = json.dumps(
[
{
"payment_doctype": "Payment Entry",
"payment_name": payment.name,
"amount": bank_transaction.unallocated_amount,
}
]
)
reconcile_vouchers(bank_transaction.name, vouchers)
unallocated_amount = frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount")
unallocated_amount = frappe.db.get_value(
"Bank Transaction", bank_transaction.name, "unallocated_amount"
)
self.assertTrue(unallocated_amount == 0)
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
@@ -69,122 +83,177 @@ class TestBankTransaction(unittest.TestCase):
# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
def test_debit_credit_output(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
linked_payments = get_linked_payments(bank_transaction.name, ['payment_entry', 'exact_match'])
bank_transaction = frappe.get_doc(
"Bank Transaction",
dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"),
)
linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"])
self.assertTrue(linked_payments[0][3])
# Check error if already reconciled
def test_already_reconciled(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
bank_transaction = frappe.get_doc(
"Bank Transaction",
dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"),
)
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
vouchers = json.dumps([{
"payment_doctype":"Payment Entry",
"payment_name":payment.name,
"amount":bank_transaction.unallocated_amount}])
vouchers = json.dumps(
[
{
"payment_doctype": "Payment Entry",
"payment_name": payment.name,
"amount": bank_transaction.unallocated_amount,
}
]
)
reconcile_vouchers(bank_transaction.name, vouchers)
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"))
bank_transaction = frappe.get_doc(
"Bank Transaction",
dict(description="1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G"),
)
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200))
vouchers = json.dumps([{
"payment_doctype":"Payment Entry",
"payment_name":payment.name,
"amount":bank_transaction.unallocated_amount}])
self.assertRaises(frappe.ValidationError, reconcile_vouchers, bank_transaction_name=bank_transaction.name, vouchers=vouchers)
vouchers = json.dumps(
[
{
"payment_doctype": "Payment Entry",
"payment_name": payment.name,
"amount": bank_transaction.unallocated_amount,
}
]
)
self.assertRaises(
frappe.ValidationError,
reconcile_vouchers,
bank_transaction_name=bank_transaction.name,
vouchers=vouchers,
)
# Raise an error if debitor transaction vs debitor payment
def test_clear_sales_invoice(self):
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"))
bank_transaction = frappe.get_doc(
"Bank Transaction",
dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio"),
)
payment = frappe.get_doc("Sales Invoice", dict(customer="Fayva", status=["=", "Paid"]))
vouchers = json.dumps([{
"payment_doctype":"Sales Invoice",
"payment_name":payment.name,
"amount":bank_transaction.unallocated_amount}])
vouchers = json.dumps(
[
{
"payment_doctype": "Sales Invoice",
"payment_name": payment.name,
"amount": bank_transaction.unallocated_amount,
}
]
)
reconcile_vouchers(bank_transaction.name, vouchers=vouchers)
self.assertEqual(frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount"), 0)
self.assertTrue(frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date") is not None)
self.assertEqual(
frappe.db.get_value("Bank Transaction", bank_transaction.name, "unallocated_amount"), 0
)
self.assertTrue(
frappe.db.get_value("Sales Invoice Payment", dict(parent=payment.name), "clearance_date")
is not None
)
def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"):
try:
frappe.get_doc({
"doctype": "Bank",
"bank_name":bank_name,
}).insert()
frappe.get_doc(
{
"doctype": "Bank",
"bank_name": bank_name,
}
).insert()
except frappe.DuplicateEntryError:
pass
try:
frappe.get_doc({
"doctype": "Bank Account",
"account_name":"Checking Account",
"bank": bank_name,
"account": account_name
}).insert()
frappe.get_doc(
{
"doctype": "Bank Account",
"account_name": "Checking Account",
"bank": bank_name,
"account": account_name,
}
).insert()
except frappe.DuplicateEntryError:
pass
def add_transactions():
create_bank_account()
doc = frappe.get_doc({
"doctype": "Bank Transaction",
"description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
"date": "2018-10-23",
"deposit": 1200,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
doc = frappe.get_doc(
{
"doctype": "Bank Transaction",
"description": "1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G",
"date": "2018-10-23",
"deposit": 1200,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank",
}
).insert()
doc.submit()
doc = frappe.get_doc({
"doctype": "Bank Transaction",
"description":"1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G",
"date": "2018-10-23",
"deposit": 1700,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
doc = frappe.get_doc(
{
"doctype": "Bank Transaction",
"description": "1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G",
"date": "2018-10-23",
"deposit": 1700,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank",
}
).insert()
doc.submit()
doc = frappe.get_doc({
"doctype": "Bank Transaction",
"description":"Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic",
"date": "2018-10-26",
"withdrawal": 690,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
doc = frappe.get_doc(
{
"doctype": "Bank Transaction",
"description": "Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic",
"date": "2018-10-26",
"withdrawal": 690,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank",
}
).insert()
doc.submit()
doc = frappe.get_doc({
"doctype": "Bank Transaction",
"description":"Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07",
"date": "2018-10-27",
"deposit": 3900,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
doc = frappe.get_doc(
{
"doctype": "Bank Transaction",
"description": "Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07",
"date": "2018-10-27",
"deposit": 3900,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank",
}
).insert()
doc.submit()
doc = frappe.get_doc({
"doctype": "Bank Transaction",
"description":"I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio",
"date": "2018-10-27",
"withdrawal": 109080,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank"
}).insert()
doc = frappe.get_doc(
{
"doctype": "Bank Transaction",
"description": "I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio",
"date": "2018-10-27",
"withdrawal": 109080,
"currency": "INR",
"bank_account": "Checking Account - Citi Bank",
}
).insert()
doc.submit()
def add_vouchers():
try:
frappe.get_doc({
"doctype": "Supplier",
"supplier_group":"All Supplier Groups",
"supplier_type": "Company",
"supplier_name": "Conrad Electronic"
}).insert()
frappe.get_doc(
{
"doctype": "Supplier",
"supplier_group": "All Supplier Groups",
"supplier_type": "Company",
"supplier_name": "Conrad Electronic",
}
).insert()
except frappe.DuplicateEntryError:
pass
@@ -198,12 +267,14 @@ def add_vouchers():
pe.submit()
try:
frappe.get_doc({
"doctype": "Supplier",
"supplier_group":"All Supplier Groups",
"supplier_type": "Company",
"supplier_name": "Mr G"
}).insert()
frappe.get_doc(
{
"doctype": "Supplier",
"supplier_group": "All Supplier Groups",
"supplier_type": "Company",
"supplier_name": "Mr G",
}
).insert()
except frappe.DuplicateEntryError:
pass
@@ -222,26 +293,30 @@ def add_vouchers():
pe.submit()
try:
frappe.get_doc({
"doctype": "Supplier",
"supplier_group":"All Supplier Groups",
"supplier_type": "Company",
"supplier_name": "Poore Simon's"
}).insert()
frappe.get_doc(
{
"doctype": "Supplier",
"supplier_group": "All Supplier Groups",
"supplier_type": "Company",
"supplier_name": "Poore Simon's",
}
).insert()
except frappe.DuplicateEntryError:
pass
try:
frappe.get_doc({
"doctype": "Customer",
"customer_group":"All Customer Groups",
"customer_type": "Company",
"customer_name": "Poore Simon's"
}).insert()
frappe.get_doc(
{
"doctype": "Customer",
"customer_group": "All Customer Groups",
"customer_type": "Company",
"customer_name": "Poore Simon's",
}
).insert()
except frappe.DuplicateEntryError:
pass
pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save =1)
pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save=1)
pi.cash_bank_account = "_Test Bank - _TC"
pi.insert()
pi.submit()
@@ -261,33 +336,31 @@ def add_vouchers():
pe.submit()
try:
frappe.get_doc({
"doctype": "Customer",
"customer_group":"All Customer Groups",
"customer_type": "Company",
"customer_name": "Fayva"
}).insert()
frappe.get_doc(
{
"doctype": "Customer",
"customer_group": "All Customer Groups",
"customer_type": "Company",
"customer_name": "Fayva",
}
).insert()
except frappe.DuplicateEntryError:
pass
mode_of_payment = frappe.get_doc({
"doctype": "Mode of Payment",
"name": "Cash"
})
mode_of_payment = frappe.get_doc({"doctype": "Mode of Payment", "name": "Cash"})
if not frappe.db.get_value('Mode of Payment Account', {'company': "_Test Company", 'parent': "Cash"}):
mode_of_payment.append("accounts", {
"company": "_Test Company",
"default_account": "_Test Bank - _TC"
})
if not frappe.db.get_value(
"Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}
):
mode_of_payment.append(
"accounts", {"company": "_Test Company", "default_account": "_Test Bank - _TC"}
)
mode_of_payment.save()
si = create_sales_invoice(customer="Fayva", qty=1, rate=109080, do_not_save=1)
si.is_pos = 1
si.append("payments", {
"mode_of_payment": "Cash",
"account": "_Test Bank - _TC",
"amount": 109080
})
si.append(
"payments", {"mode_of_payment": "Cash", "account": "_Test Bank - _TC", "amount": 109080}
)
si.insert()
si.submit()

View File

@@ -14,13 +14,19 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
from erpnext.accounts.utils import get_fiscal_year
class BudgetError(frappe.ValidationError): pass
class DuplicateBudgetError(frappe.ValidationError): pass
class BudgetError(frappe.ValidationError):
pass
class DuplicateBudgetError(frappe.ValidationError):
pass
class Budget(Document):
def autoname(self):
self.name = make_autoname(self.get(frappe.scrub(self.budget_against))
+ "/" + self.fiscal_year + "/.###")
self.name = make_autoname(
self.get(frappe.scrub(self.budget_against)) + "/" + self.fiscal_year + "/.###"
)
def validate(self):
if not self.get(frappe.scrub(self.budget_against)):
@@ -35,34 +41,44 @@ class Budget(Document):
budget_against = self.get(budget_against_field)
accounts = [d.account for d in self.accounts] or []
existing_budget = frappe.db.sql("""
existing_budget = frappe.db.sql(
"""
select
b.name, ba.account from `tabBudget` b, `tabBudget Account` ba
where
ba.parent = b.name and b.docstatus < 2 and b.company = %s and %s=%s and
b.fiscal_year=%s and b.name != %s and ba.account in (%s) """
% ('%s', budget_against_field, '%s', '%s', '%s', ','.join(['%s'] * len(accounts))),
(self.company, budget_against, self.fiscal_year, self.name) + tuple(accounts), as_dict=1)
% ("%s", budget_against_field, "%s", "%s", "%s", ",".join(["%s"] * len(accounts))),
(self.company, budget_against, self.fiscal_year, self.name) + tuple(accounts),
as_dict=1,
)
for d in existing_budget:
frappe.throw(_("Another Budget record '{0}' already exists against {1} '{2}' and account '{3}' for fiscal year {4}")
.format(d.name, self.budget_against, budget_against, d.account, self.fiscal_year), DuplicateBudgetError)
frappe.throw(
_(
"Another Budget record '{0}' already exists against {1} '{2}' and account '{3}' for fiscal year {4}"
).format(d.name, self.budget_against, budget_against, d.account, self.fiscal_year),
DuplicateBudgetError,
)
def validate_accounts(self):
account_list = []
for d in self.get('accounts'):
for d in self.get("accounts"):
if d.account:
account_details = frappe.db.get_value("Account", d.account,
["is_group", "company", "report_type"], as_dict=1)
account_details = frappe.db.get_value(
"Account", d.account, ["is_group", "company", "report_type"], as_dict=1
)
if account_details.is_group:
frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account))
elif account_details.company != self.company:
frappe.throw(_("Account {0} does not belongs to company {1}")
.format(d.account, self.company))
frappe.throw(_("Account {0} does not belongs to company {1}").format(d.account, self.company))
elif account_details.report_type != "Profit and Loss":
frappe.throw(_("Budget cannot be assigned against {0}, as it's not an Income or Expense account")
.format(d.account))
frappe.throw(
_("Budget cannot be assigned against {0}, as it's not an Income or Expense account").format(
d.account
)
)
if d.account in account_list:
frappe.throw(_("Account {0} has been entered multiple times").format(d.account))
@@ -70,51 +86,66 @@ class Budget(Document):
account_list.append(d.account)
def set_null_value(self):
if self.budget_against == 'Cost Center':
if self.budget_against == "Cost Center":
self.project = None
else:
self.cost_center = None
def validate_applicable_for(self):
if (self.applicable_on_material_request
and not (self.applicable_on_purchase_order and self.applicable_on_booking_actual_expenses)):
frappe.throw(_("Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses"))
if self.applicable_on_material_request and not (
self.applicable_on_purchase_order and self.applicable_on_booking_actual_expenses
):
frappe.throw(
_("Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses")
)
elif (self.applicable_on_purchase_order
and not (self.applicable_on_booking_actual_expenses)):
elif self.applicable_on_purchase_order and not (self.applicable_on_booking_actual_expenses):
frappe.throw(_("Please enable Applicable on Booking Actual Expenses"))
elif not(self.applicable_on_material_request
or self.applicable_on_purchase_order or self.applicable_on_booking_actual_expenses):
elif not (
self.applicable_on_material_request
or self.applicable_on_purchase_order
or self.applicable_on_booking_actual_expenses
):
self.applicable_on_booking_actual_expenses = 1
def validate_expense_against_budget(args):
args = frappe._dict(args)
if args.get('company') and not args.fiscal_year:
args.fiscal_year = get_fiscal_year(args.get('posting_date'), company=args.get('company'))[0]
frappe.flags.exception_approver_role = frappe.get_cached_value('Company',
args.get('company'), 'exception_budget_approver_role')
if args.get("company") and not args.fiscal_year:
args.fiscal_year = get_fiscal_year(args.get("posting_date"), company=args.get("company"))[0]
frappe.flags.exception_approver_role = frappe.get_cached_value(
"Company", args.get("company"), "exception_budget_approver_role"
)
if not args.account:
args.account = args.get("expense_account")
if not (args.get('account') and args.get('cost_center')) and args.item_code:
if not (args.get("account") and args.get("cost_center")) and args.item_code:
args.cost_center, args.account = get_item_details(args)
if not args.account:
return
for budget_against in ['project', 'cost_center'] + get_accounting_dimensions():
if (args.get(budget_against) and args.account
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})):
for budget_against in ["project", "cost_center"] + get_accounting_dimensions():
if (
args.get(budget_against)
and args.account
and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"})
):
doctype = frappe.unscrub(budget_against)
if frappe.get_cached_value('DocType', doctype, 'is_tree'):
if frappe.get_cached_value("DocType", doctype, "is_tree"):
lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"])
condition = """and exists(select name from `tab%s`
where lft<=%s and rgt>=%s and name=b.%s)""" % (doctype, lft, rgt, budget_against) #nosec
where lft<=%s and rgt>=%s and name=b.%s)""" % (
doctype,
lft,
rgt,
budget_against,
) # nosec
args.is_tree = True
else:
condition = "and b.%s=%s" % (budget_against, frappe.db.escape(args.get(budget_against)))
@@ -123,7 +154,8 @@ def validate_expense_against_budget(args):
args.budget_against_field = budget_against
args.budget_against_doctype = doctype
budget_records = frappe.db.sql("""
budget_records = frappe.db.sql(
"""
select
b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution,
ifnull(b.applicable_on_material_request, 0) as for_material_request,
@@ -138,11 +170,17 @@ def validate_expense_against_budget(args):
b.name=ba.parent and b.fiscal_year=%s
and ba.account=%s and b.docstatus=1
{condition}
""".format(condition=condition, budget_against_field=budget_against), (args.fiscal_year, args.account), as_dict=True) #nosec
""".format(
condition=condition, budget_against_field=budget_against
),
(args.fiscal_year, args.account),
as_dict=True,
) # nosec
if budget_records:
validate_budget_records(args, budget_records)
def validate_budget_records(args, budget_records):
for budget in budget_records:
if flt(budget.budget_amount):
@@ -150,88 +188,118 @@ def validate_budget_records(args, budget_records):
yearly_action, monthly_action = get_actions(args, budget)
if monthly_action in ["Stop", "Warn"]:
budget_amount = get_accumulated_monthly_budget(budget.monthly_distribution,
args.posting_date, args.fiscal_year, budget.budget_amount)
budget_amount = get_accumulated_monthly_budget(
budget.monthly_distribution, args.posting_date, args.fiscal_year, budget.budget_amount
)
args["month_end_date"] = get_last_day(args.posting_date)
compare_expense_with_budget(args, budget_amount,
_("Accumulated Monthly"), monthly_action, budget.budget_against, amount)
compare_expense_with_budget(
args, budget_amount, _("Accumulated Monthly"), monthly_action, budget.budget_against, amount
)
if (
yearly_action in ("Stop", "Warn")
and monthly_action != "Stop"
and yearly_action != monthly_action
):
compare_expense_with_budget(
args, flt(budget.budget_amount), _("Annual"), yearly_action, budget.budget_against, amount
)
if yearly_action in ("Stop", "Warn") and monthly_action != "Stop" \
and yearly_action != monthly_action:
compare_expense_with_budget(args, flt(budget.budget_amount),
_("Annual"), yearly_action, budget.budget_against, amount)
def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against, amount=0):
actual_expense = amount or get_actual_expense(args)
if actual_expense > budget_amount:
diff = actual_expense - budget_amount
currency = frappe.get_cached_value('Company', args.company, 'default_currency')
currency = frappe.get_cached_value("Company", args.company, "default_currency")
msg = _("{0} Budget for Account {1} against {2} {3} is {4}. It will exceed by {5}").format(
_(action_for), frappe.bold(args.account), args.budget_against_field,
frappe.bold(budget_against),
frappe.bold(fmt_money(budget_amount, currency=currency)),
frappe.bold(fmt_money(diff, currency=currency)))
_(action_for),
frappe.bold(args.account),
args.budget_against_field,
frappe.bold(budget_against),
frappe.bold(fmt_money(budget_amount, currency=currency)),
frappe.bold(fmt_money(diff, currency=currency)),
)
if (frappe.flags.exception_approver_role
and frappe.flags.exception_approver_role in frappe.get_roles(frappe.session.user)):
if (
frappe.flags.exception_approver_role
and frappe.flags.exception_approver_role in frappe.get_roles(frappe.session.user)
):
action = "Warn"
if action=="Stop":
if action == "Stop":
frappe.throw(msg, BudgetError)
else:
frappe.msgprint(msg, indicator='orange')
frappe.msgprint(msg, indicator="orange")
def get_actions(args, budget):
yearly_action = budget.action_if_annual_budget_exceeded
monthly_action = budget.action_if_accumulated_monthly_budget_exceeded
if args.get('doctype') == 'Material Request' and budget.for_material_request:
if args.get("doctype") == "Material Request" and budget.for_material_request:
yearly_action = budget.action_if_annual_budget_exceeded_on_mr
monthly_action = budget.action_if_accumulated_monthly_budget_exceeded_on_mr
elif args.get('doctype') == 'Purchase Order' and budget.for_purchase_order:
elif args.get("doctype") == "Purchase Order" and budget.for_purchase_order:
yearly_action = budget.action_if_annual_budget_exceeded_on_po
monthly_action = budget.action_if_accumulated_monthly_budget_exceeded_on_po
return yearly_action, monthly_action
def get_amount(args, budget):
amount = 0
if args.get('doctype') == 'Material Request' and budget.for_material_request:
amount = (get_requested_amount(args, budget)
+ get_ordered_amount(args, budget) + get_actual_expense(args))
if args.get("doctype") == "Material Request" and budget.for_material_request:
amount = (
get_requested_amount(args, budget) + get_ordered_amount(args, budget) + get_actual_expense(args)
)
elif args.get('doctype') == 'Purchase Order' and budget.for_purchase_order:
elif args.get("doctype") == "Purchase Order" and budget.for_purchase_order:
amount = get_ordered_amount(args, budget) + get_actual_expense(args)
return amount
def get_requested_amount(args, budget):
item_code = args.get('item_code')
condition = get_other_condition(args, budget, 'Material Request')
data = frappe.db.sql(""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount
def get_requested_amount(args, budget):
item_code = args.get("item_code")
condition = get_other_condition(args, budget, "Material Request")
data = frappe.db.sql(
""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount
from `tabMaterial Request Item` child, `tabMaterial Request` parent where parent.name = child.parent and
child.item_code = %s and parent.docstatus = 1 and child.stock_qty > child.ordered_qty and {0} and
parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(condition), item_code, as_list=1)
parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(
condition
),
item_code,
as_list=1,
)
return data[0][0] if data else 0
def get_ordered_amount(args, budget):
item_code = args.get('item_code')
condition = get_other_condition(args, budget, 'Purchase Order')
item_code = args.get("item_code")
condition = get_other_condition(args, budget, "Purchase Order")
data = frappe.db.sql(""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
data = frappe.db.sql(
""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
from `tabPurchase Order Item` child, `tabPurchase Order` parent where
parent.name = child.parent and child.item_code = %s and parent.docstatus = 1 and child.amount > child.billed_amt
and parent.status != 'Closed' and {0}""".format(condition), item_code, as_list=1)
and parent.status != 'Closed' and {0}""".format(
condition
),
item_code,
as_list=1,
)
return data[0][0] if data else 0
def get_other_condition(args, budget, for_doc):
condition = "expense_account = '%s'" % (args.expense_account)
budget_against_field = args.get("budget_against_field")
@@ -239,41 +307,51 @@ def get_other_condition(args, budget, for_doc):
if budget_against_field and args.get(budget_against_field):
condition += " and child.%s = '%s'" % (budget_against_field, args.get(budget_against_field))
if args.get('fiscal_year'):
date_field = 'schedule_date' if for_doc == 'Material Request' else 'transaction_date'
start_date, end_date = frappe.db.get_value('Fiscal Year', args.get('fiscal_year'),
['year_start_date', 'year_end_date'])
if args.get("fiscal_year"):
date_field = "schedule_date" if for_doc == "Material Request" else "transaction_date"
start_date, end_date = frappe.db.get_value(
"Fiscal Year", args.get("fiscal_year"), ["year_start_date", "year_end_date"]
)
condition += """ and parent.%s
between '%s' and '%s' """ %(date_field, start_date, end_date)
between '%s' and '%s' """ % (
date_field,
start_date,
end_date,
)
return condition
def get_actual_expense(args):
if not args.budget_against_doctype:
args.budget_against_doctype = frappe.unscrub(args.budget_against_field)
budget_against_field = args.get('budget_against_field')
condition1 = " and gle.posting_date <= %(month_end_date)s" \
if args.get("month_end_date") else ""
budget_against_field = args.get("budget_against_field")
condition1 = " and gle.posting_date <= %(month_end_date)s" if args.get("month_end_date") else ""
if args.is_tree:
lft_rgt = frappe.db.get_value(args.budget_against_doctype,
args.get(budget_against_field), ["lft", "rgt"], as_dict=1)
lft_rgt = frappe.db.get_value(
args.budget_against_doctype, args.get(budget_against_field), ["lft", "rgt"], as_dict=1
)
args.update(lft_rgt)
condition2 = """and exists(select name from `tab{doctype}`
where lft>=%(lft)s and rgt<=%(rgt)s
and name=gle.{budget_against_field})""".format(doctype=args.budget_against_doctype, #nosec
budget_against_field=budget_against_field)
and name=gle.{budget_against_field})""".format(
doctype=args.budget_against_doctype, budget_against_field=budget_against_field # nosec
)
else:
condition2 = """and exists(select name from `tab{doctype}`
where name=gle.{budget_against} and
gle.{budget_against} = %({budget_against})s)""".format(doctype=args.budget_against_doctype,
budget_against = budget_against_field)
gle.{budget_against} = %({budget_against})s)""".format(
doctype=args.budget_against_doctype, budget_against=budget_against_field
)
amount = flt(frappe.db.sql("""
amount = flt(
frappe.db.sql(
"""
select sum(gle.debit) - sum(gle.credit)
from `tabGL Entry` gle
where gle.account=%(account)s
@@ -282,46 +360,59 @@ def get_actual_expense(args):
and gle.company=%(company)s
and gle.docstatus=1
{condition2}
""".format(condition1=condition1, condition2=condition2), (args))[0][0]) #nosec
""".format(
condition1=condition1, condition2=condition2
),
(args),
)[0][0]
) # nosec
return amount
def get_accumulated_monthly_budget(monthly_distribution, posting_date, fiscal_year, annual_budget):
distribution = {}
if monthly_distribution:
for d in frappe.db.sql("""select mdp.month, mdp.percentage_allocation
for d in frappe.db.sql(
"""select mdp.month, mdp.percentage_allocation
from `tabMonthly Distribution Percentage` mdp, `tabMonthly Distribution` md
where mdp.parent=md.name and md.fiscal_year=%s""", fiscal_year, as_dict=1):
distribution.setdefault(d.month, d.percentage_allocation)
where mdp.parent=md.name and md.fiscal_year=%s""",
fiscal_year,
as_dict=1,
):
distribution.setdefault(d.month, d.percentage_allocation)
dt = frappe.db.get_value("Fiscal Year", fiscal_year, "year_start_date")
accumulated_percentage = 0.0
while(dt <= getdate(posting_date)):
while dt <= getdate(posting_date):
if monthly_distribution:
accumulated_percentage += distribution.get(getdate(dt).strftime("%B"), 0)
else:
accumulated_percentage += 100.0/12
accumulated_percentage += 100.0 / 12
dt = add_months(dt, 1)
return annual_budget * accumulated_percentage / 100
def get_item_details(args):
cost_center, expense_account = None, None
if not args.get('company'):
if not args.get("company"):
return cost_center, expense_account
if args.item_code:
item_defaults = frappe.db.get_value('Item Default',
{'parent': args.item_code, 'company': args.get('company')},
['buying_cost_center', 'expense_account'])
item_defaults = frappe.db.get_value(
"Item Default",
{"parent": args.item_code, "company": args.get("company")},
["buying_cost_center", "expense_account"],
)
if item_defaults:
cost_center, expense_account = item_defaults
if not (cost_center and expense_account):
for doctype in ['Item Group', 'Company']:
for doctype in ["Item Group", "Company"]:
data = get_expense_cost_center(doctype, args)
if not cost_center and data:
@@ -335,11 +426,15 @@ def get_item_details(args):
return cost_center, expense_account
def get_expense_cost_center(doctype, args):
if doctype == 'Item Group':
return frappe.db.get_value('Item Default',
{'parent': args.get(frappe.scrub(doctype)), 'company': args.get('company')},
['buying_cost_center', 'expense_account'])
if doctype == "Item Group":
return frappe.db.get_value(
"Item Default",
{"parent": args.get(frappe.scrub(doctype)), "company": args.get("company")},
["buying_cost_center", "expense_account"],
)
else:
return frappe.db.get_value(doctype, args.get(frappe.scrub(doctype)),\
['cost_center', 'default_expense_account'])
return frappe.db.get_value(
doctype, args.get(frappe.scrub(doctype)), ["cost_center", "default_expense_account"]
)

View File

@@ -11,7 +11,8 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
from erpnext.accounts.utils import get_fiscal_year
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
test_dependencies = ['Monthly Distribution']
test_dependencies = ["Monthly Distribution"]
class TestBudget(unittest.TestCase):
def test_monthly_budget_crossed_ignore(self):
@@ -19,11 +20,18 @@ class TestBudget(unittest.TestCase):
budget = make_budget(budget_against="Cost Center")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC",
40000,
"_Test Cost Center - _TC",
posting_date=nowdate(),
submit=True,
)
self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
self.assertTrue(
frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
)
budget.cancel()
jv.cancel()
@@ -33,10 +41,17 @@ class TestBudget(unittest.TestCase):
budget = make_budget(budget_against="Cost Center")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
frappe.db.set_value(
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate())
jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC",
40000,
"_Test Cost Center - _TC",
posting_date=nowdate(),
)
self.assertRaises(BudgetError, jv.submit)
@@ -48,49 +63,65 @@ class TestBudget(unittest.TestCase):
budget = make_budget(budget_against="Cost Center")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
frappe.db.set_value(
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date=nowdate())
jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC",
40000,
"_Test Cost Center - _TC",
posting_date=nowdate(),
)
self.assertRaises(BudgetError, jv.submit)
frappe.db.set_value('Company', budget.company, 'exception_budget_approver_role', 'Accounts User')
frappe.db.set_value("Company", budget.company, "exception_budget_approver_role", "Accounts User")
jv.submit()
self.assertEqual(frappe.db.get_value('Journal Entry', jv.name, 'docstatus'), 1)
self.assertEqual(frappe.db.get_value("Journal Entry", jv.name, "docstatus"), 1)
jv.cancel()
frappe.db.set_value('Company', budget.company, 'exception_budget_approver_role', '')
frappe.db.set_value("Company", budget.company, "exception_budget_approver_role", "")
budget.load_from_db()
budget.cancel()
def test_monthly_budget_crossed_for_mr(self):
budget = make_budget(applicable_on_material_request=1,
applicable_on_purchase_order=1, action_if_accumulated_monthly_budget_exceeded_on_mr="Stop",
budget_against="Cost Center")
budget = make_budget(
applicable_on_material_request=1,
applicable_on_purchase_order=1,
action_if_accumulated_monthly_budget_exceeded_on_mr="Stop",
budget_against="Cost Center",
)
fiscal_year = get_fiscal_year(nowdate())[0]
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
frappe.db.set_value(
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
mr = frappe.get_doc({
"doctype": "Material Request",
"material_request_type": "Purchase",
"transaction_date": nowdate(),
"company": budget.company,
"items": [{
'item_code': '_Test Item',
'qty': 1,
'uom': "_Test UOM",
'warehouse': '_Test Warehouse - _TC',
'schedule_date': nowdate(),
'rate': 100000,
'expense_account': '_Test Account Cost for Goods Sold - _TC',
'cost_center': '_Test Cost Center - _TC'
}]
})
mr = frappe.get_doc(
{
"doctype": "Material Request",
"material_request_type": "Purchase",
"transaction_date": nowdate(),
"company": budget.company,
"items": [
{
"item_code": "_Test Item",
"qty": 1,
"uom": "_Test UOM",
"warehouse": "_Test Warehouse - _TC",
"schedule_date": nowdate(),
"rate": 100000,
"expense_account": "_Test Account Cost for Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
}
],
}
)
mr.set_missing_values()
@@ -100,11 +131,16 @@ class TestBudget(unittest.TestCase):
budget.cancel()
def test_monthly_budget_crossed_for_po(self):
budget = make_budget(applicable_on_purchase_order=1,
action_if_accumulated_monthly_budget_exceeded_on_po="Stop", budget_against="Cost Center")
budget = make_budget(
applicable_on_purchase_order=1,
action_if_accumulated_monthly_budget_exceeded_on_po="Stop",
budget_against="Cost Center",
)
fiscal_year = get_fiscal_year(nowdate())[0]
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
frappe.db.set_value(
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
po = create_purchase_order(transaction_date=nowdate(), do_not_submit=True)
@@ -122,12 +158,20 @@ class TestBudget(unittest.TestCase):
budget = make_budget(budget_against="Project")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
frappe.db.set_value(
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
project = frappe.get_value("Project", {"project_name": "_Test Project"})
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project=project, posting_date=nowdate())
jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC",
40000,
"_Test Cost Center - _TC",
project=project,
posting_date=nowdate(),
)
self.assertRaises(BudgetError, jv.submit)
@@ -139,8 +183,13 @@ class TestBudget(unittest.TestCase):
budget = make_budget(budget_against="Cost Center")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 250000, "_Test Cost Center - _TC", posting_date=nowdate())
jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC",
250000,
"_Test Cost Center - _TC",
posting_date=nowdate(),
)
self.assertRaises(BudgetError, jv.submit)
@@ -153,9 +202,14 @@ class TestBudget(unittest.TestCase):
project = frappe.get_value("Project", {"project_name": "_Test Project"})
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 250000, "_Test Cost Center - _TC",
project=project, posting_date=nowdate())
jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC",
250000,
"_Test Cost Center - _TC",
project=project,
posting_date=nowdate(),
)
self.assertRaises(BudgetError, jv.submit)
@@ -169,14 +223,23 @@ class TestBudget(unittest.TestCase):
if month > 9:
month = 9
for i in range(month+1):
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
for i in range(month + 1):
jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC",
20000,
"_Test Cost Center - _TC",
posting_date=nowdate(),
submit=True,
)
self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
self.assertTrue(
frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
)
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
frappe.db.set_value(
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
self.assertRaises(BudgetError, jv.cancel)
@@ -193,14 +256,23 @@ class TestBudget(unittest.TestCase):
project = frappe.get_value("Project", {"project_name": "_Test Project"})
for i in range(month + 1):
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True,
project=project)
jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC",
20000,
"_Test Cost Center - _TC",
posting_date=nowdate(),
submit=True,
project=project,
)
self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv.name}))
self.assertTrue(
frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
)
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
frappe.db.set_value(
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
self.assertRaises(BudgetError, jv.cancel)
@@ -212,10 +284,17 @@ class TestBudget(unittest.TestCase):
set_total_expense_zero(nowdate(), "cost_center", "_Test Cost Center 2 - _TC")
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
frappe.db.set_value(
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, "_Test Cost Center 2 - _TC", posting_date=nowdate())
jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC",
40000,
"_Test Cost Center 2 - _TC",
posting_date=nowdate(),
)
self.assertRaises(BudgetError, jv.submit)
@@ -226,19 +305,28 @@ class TestBudget(unittest.TestCase):
cost_center = "_Test Cost Center 3 - _TC"
if not frappe.db.exists("Cost Center", cost_center):
frappe.get_doc({
'doctype': 'Cost Center',
'cost_center_name': '_Test Cost Center 3',
'parent_cost_center': "_Test Company - _TC",
'company': '_Test Company',
'is_group': 0
}).insert(ignore_permissions=True)
frappe.get_doc(
{
"doctype": "Cost Center",
"cost_center_name": "_Test Cost Center 3",
"parent_cost_center": "_Test Company - _TC",
"company": "_Test Company",
"is_group": 0,
}
).insert(ignore_permissions=True)
budget = make_budget(budget_against="Cost Center", cost_center=cost_center)
frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
frappe.db.set_value(
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 40000, cost_center, posting_date=nowdate())
jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC",
40000,
cost_center,
posting_date=nowdate(),
)
self.assertRaises(BudgetError, jv.submit)
@@ -255,14 +343,16 @@ def set_total_expense_zero(posting_date, budget_against_field=None, budget_again
fiscal_year = get_fiscal_year(nowdate())[0]
args = frappe._dict({
"account": "_Test Account Cost for Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
"monthly_end_date": posting_date,
"company": "_Test Company",
"fiscal_year": fiscal_year,
"budget_against_field": budget_against_field,
})
args = frappe._dict(
{
"account": "_Test Account Cost for Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
"monthly_end_date": posting_date,
"company": "_Test Company",
"fiscal_year": fiscal_year,
"budget_against_field": budget_against_field,
}
)
if not args.get(budget_against_field):
args[budget_against_field] = budget_against
@@ -271,26 +361,42 @@ def set_total_expense_zero(posting_date, budget_against_field=None, budget_again
if existing_expense:
if budget_against_field == "cost_center":
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
make_journal_entry(
"_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC",
-existing_expense,
"_Test Cost Center - _TC",
posting_date=nowdate(),
submit=True,
)
elif budget_against_field == "project":
make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project=budget_against, posting_date=nowdate())
make_journal_entry(
"_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC",
-existing_expense,
"_Test Cost Center - _TC",
submit=True,
project=budget_against,
posting_date=nowdate(),
)
def make_budget(**args):
args = frappe._dict(args)
budget_against=args.budget_against
cost_center=args.cost_center
budget_against = args.budget_against
cost_center = args.cost_center
fiscal_year = get_fiscal_year(nowdate())[0]
if budget_against == "Project":
project_name = "{0}%".format("_Test Project/" + fiscal_year)
budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", project_name)})
budget_list = frappe.get_all("Budget", fields=["name"], filters={"name": ("like", project_name)})
else:
cost_center_name = "{0}%".format(cost_center or "_Test Cost Center - _TC/" + fiscal_year)
budget_list = frappe.get_all("Budget", fields=["name"], filters = {"name": ("like", cost_center_name)})
budget_list = frappe.get_all(
"Budget", fields=["name"], filters={"name": ("like", cost_center_name)}
)
for d in budget_list:
frappe.db.sql("delete from `tabBudget` where name = %(name)s", d)
frappe.db.sql("delete from `tabBudget Account` where parent = %(name)s", d)
@@ -300,7 +406,7 @@ def make_budget(**args):
if budget_against == "Project":
budget.project = frappe.get_value("Project", {"project_name": "_Test Project"})
else:
budget.cost_center =cost_center or "_Test Cost Center - _TC"
budget.cost_center = cost_center or "_Test Cost Center - _TC"
monthly_distribution = frappe.get_doc("Monthly Distribution", "_Test Distribution")
monthly_distribution.fiscal_year = fiscal_year
@@ -312,20 +418,27 @@ def make_budget(**args):
budget.action_if_annual_budget_exceeded = "Stop"
budget.action_if_accumulated_monthly_budget_exceeded = "Ignore"
budget.budget_against = budget_against
budget.append("accounts", {
"account": "_Test Account Cost for Goods Sold - _TC",
"budget_amount": 200000
})
budget.append(
"accounts", {"account": "_Test Account Cost for Goods Sold - _TC", "budget_amount": 200000}
)
if args.applicable_on_material_request:
budget.applicable_on_material_request = 1
budget.action_if_annual_budget_exceeded_on_mr = args.action_if_annual_budget_exceeded_on_mr or 'Warn'
budget.action_if_accumulated_monthly_budget_exceeded_on_mr = args.action_if_accumulated_monthly_budget_exceeded_on_mr or 'Warn'
budget.action_if_annual_budget_exceeded_on_mr = (
args.action_if_annual_budget_exceeded_on_mr or "Warn"
)
budget.action_if_accumulated_monthly_budget_exceeded_on_mr = (
args.action_if_accumulated_monthly_budget_exceeded_on_mr or "Warn"
)
if args.applicable_on_purchase_order:
budget.applicable_on_purchase_order = 1
budget.action_if_annual_budget_exceeded_on_po = args.action_if_annual_budget_exceeded_on_po or 'Warn'
budget.action_if_accumulated_monthly_budget_exceeded_on_po = args.action_if_accumulated_monthly_budget_exceeded_on_po or 'Warn'
budget.action_if_annual_budget_exceeded_on_po = (
args.action_if_annual_budget_exceeded_on_po or "Warn"
)
budget.action_if_accumulated_monthly_budget_exceeded_on_po = (
args.action_if_accumulated_monthly_budget_exceeded_on_po or "Warn"
)
budget.insert()
budget.submit()

View File

@@ -11,28 +11,42 @@ from frappe.utils import flt
class CForm(Document):
def validate(self):
"""Validate invoice that c-form is applicable
and no other c-form is received for that"""
and no other c-form is received for that"""
for d in self.get('invoices'):
for d in self.get("invoices"):
if d.invoice_no:
inv = frappe.db.sql("""select c_form_applicable, c_form_no from
`tabSales Invoice` where name = %s and docstatus = 1""", d.invoice_no)
inv = frappe.db.sql(
"""select c_form_applicable, c_form_no from
`tabSales Invoice` where name = %s and docstatus = 1""",
d.invoice_no,
)
if inv and inv[0][0] != 'Yes':
if inv and inv[0][0] != "Yes":
frappe.throw(_("C-form is not applicable for Invoice: {0}").format(d.invoice_no))
elif inv and inv[0][1] and inv[0][1] != self.name:
frappe.throw(_("""Invoice {0} is tagged in another C-form: {1}.
frappe.throw(
_(
"""Invoice {0} is tagged in another C-form: {1}.
If you want to change C-form no for this invoice,
please remove invoice no from the previous c-form and then try again"""\
.format(d.invoice_no, inv[0][1])))
please remove invoice no from the previous c-form and then try again""".format(
d.invoice_no, inv[0][1]
)
)
)
elif not inv:
frappe.throw(_("Row {0}: Invoice {1} is invalid, it might be cancelled / does not exist. \
Please enter a valid Invoice".format(d.idx, d.invoice_no)))
frappe.throw(
_(
"Row {0}: Invoice {1} is invalid, it might be cancelled / does not exist. \
Please enter a valid Invoice".format(
d.idx, d.invoice_no
)
)
)
def on_update(self):
""" Update C-Form No on invoices"""
"""Update C-Form No on invoices"""
self.set_total_invoiced_amount()
def on_submit(self):
@@ -43,30 +57,40 @@ class CForm(Document):
frappe.db.sql("""update `tabSales Invoice` set c_form_no=null where c_form_no=%s""", self.name)
def set_cform_in_sales_invoices(self):
inv = [d.invoice_no for d in self.get('invoices')]
inv = [d.invoice_no for d in self.get("invoices")]
if inv:
frappe.db.sql("""update `tabSales Invoice` set c_form_no=%s, modified=%s where name in (%s)""" %
('%s', '%s', ', '.join(['%s'] * len(inv))), tuple([self.name, self.modified] + inv))
frappe.db.sql(
"""update `tabSales Invoice` set c_form_no=%s, modified=%s where name in (%s)"""
% ("%s", "%s", ", ".join(["%s"] * len(inv))),
tuple([self.name, self.modified] + inv),
)
frappe.db.sql("""update `tabSales Invoice` set c_form_no = null, modified = %s
where name not in (%s) and ifnull(c_form_no, '') = %s""" %
('%s', ', '.join(['%s']*len(inv)), '%s'), tuple([self.modified] + inv + [self.name]))
frappe.db.sql(
"""update `tabSales Invoice` set c_form_no = null, modified = %s
where name not in (%s) and ifnull(c_form_no, '') = %s"""
% ("%s", ", ".join(["%s"] * len(inv)), "%s"),
tuple([self.modified] + inv + [self.name]),
)
else:
frappe.throw(_("Please enter atleast 1 invoice in the table"))
def set_total_invoiced_amount(self):
total = sum(flt(d.grand_total) for d in self.get('invoices'))
frappe.db.set(self, 'total_invoiced_amount', total)
total = sum(flt(d.grand_total) for d in self.get("invoices"))
frappe.db.set(self, "total_invoiced_amount", total)
@frappe.whitelist()
def get_invoice_details(self, invoice_no):
""" Pull details from invoices for referrence """
"""Pull details from invoices for referrence"""
if invoice_no:
inv = frappe.db.get_value("Sales Invoice", invoice_no,
["posting_date", "territory", "base_net_total", "base_grand_total"], as_dict=True)
inv = frappe.db.get_value(
"Sales Invoice",
invoice_no,
["posting_date", "territory", "base_net_total", "base_grand_total"],
as_dict=True,
)
return {
'invoice_date' : inv.posting_date,
'territory' : inv.territory,
'net_total' : inv.base_net_total,
'grand_total' : inv.base_grand_total
"invoice_date": inv.posting_date,
"territory": inv.territory,
"net_total": inv.base_net_total,
"grand_total": inv.base_grand_total,
}

View File

@@ -5,5 +5,6 @@ import unittest
# test_records = frappe.get_test_records('C-Form')
class TestCForm(unittest.TestCase):
pass

View File

@@ -1,26 +1,25 @@
DEFAULT_MAPPERS = [
{
'doctype': 'Cash Flow Mapper',
'section_footer': 'Net cash generated by operating activities',
'section_header': 'Cash flows from operating activities',
'section_leader': 'Adjustments for',
'section_name': 'Operating Activities',
'position': 0,
'section_subtotal': 'Cash generated from operations',
},
{
'doctype': 'Cash Flow Mapper',
'position': 1,
'section_footer': 'Net cash used in investing activities',
'section_header': 'Cash flows from investing activities',
'section_name': 'Investing Activities'
},
{
'doctype': 'Cash Flow Mapper',
'position': 2,
'section_footer': 'Net cash used in financing activites',
'section_header': 'Cash flows from financing activities',
'section_name': 'Financing Activities',
}
{
"doctype": "Cash Flow Mapper",
"section_footer": "Net cash generated by operating activities",
"section_header": "Cash flows from operating activities",
"section_leader": "Adjustments for",
"section_name": "Operating Activities",
"position": 0,
"section_subtotal": "Cash generated from operations",
},
{
"doctype": "Cash Flow Mapper",
"position": 1,
"section_footer": "Net cash used in investing activities",
"section_header": "Cash flows from investing activities",
"section_name": "Investing Activities",
},
{
"doctype": "Cash Flow Mapper",
"position": 2,
"section_footer": "Net cash used in financing activites",
"section_header": "Cash flows from financing activities",
"section_name": "Financing Activities",
},
]

View File

@@ -11,9 +11,11 @@ class CashFlowMapping(Document):
self.validate_checked_options()
def validate_checked_options(self):
checked_fields = [d for d in self.meta.fields if d.fieldtype == 'Check' and self.get(d.fieldname) == 1]
checked_fields = [
d for d in self.meta.fields if d.fieldtype == "Check" and self.get(d.fieldname) == 1
]
if len(checked_fields) > 1:
frappe.throw(
frappe._('You can only select a maximum of one option from the list of check boxes.'),
title='Error'
frappe._("You can only select a maximum of one option from the list of check boxes."),
title="Error",
)

View File

@@ -9,19 +9,16 @@ import frappe
class TestCashFlowMapping(unittest.TestCase):
def setUp(self):
if frappe.db.exists("Cash Flow Mapping", "Test Mapping"):
frappe.delete_doc('Cash Flow Mappping', 'Test Mapping')
frappe.delete_doc("Cash Flow Mappping", "Test Mapping")
def tearDown(self):
frappe.delete_doc('Cash Flow Mapping', 'Test Mapping')
frappe.delete_doc("Cash Flow Mapping", "Test Mapping")
def test_multiple_selections_not_allowed(self):
doc = frappe.new_doc('Cash Flow Mapping')
doc.mapping_name = 'Test Mapping'
doc.label = 'Test label'
doc.append(
'accounts',
{'account': 'Accounts Receivable - _TC'}
)
doc = frappe.new_doc("Cash Flow Mapping")
doc.mapping_name = "Test Mapping"
doc.label = "Test label"
doc.append("accounts", {"account": "Accounts Receivable - _TC"})
doc.is_working_capital = 1
doc.is_finance_cost = 1

View File

@@ -17,11 +17,14 @@ class CashierClosing(Document):
self.make_calculations()
def get_outstanding(self):
values = frappe.db.sql("""
values = frappe.db.sql(
"""
select sum(outstanding_amount)
from `tabSales Invoice`
where posting_date=%s and posting_time>=%s and posting_time<=%s and owner=%s
""", (self.date, self.from_time, self.time, self.user))
""",
(self.date, self.from_time, self.time, self.user),
)
self.outstanding_amount = flt(values[0][0] if values else 0)
def make_calculations(self):
@@ -29,7 +32,9 @@ class CashierClosing(Document):
for i in self.payments:
total += flt(i.amount)
self.net_amount = total + self.outstanding_amount + flt(self.expense) - flt(self.custody) + flt(self.returns)
self.net_amount = (
total + self.outstanding_amount + flt(self.expense) - flt(self.custody) + flt(self.returns)
)
def validate_time(self):
if self.from_time >= self.time:

View File

@@ -25,33 +25,41 @@ from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import
class ChartofAccountsImporter(Document):
def validate(self):
if self.import_file:
get_coa('Chart of Accounts Importer', 'All Accounts', file_name=self.import_file, for_validate=1)
get_coa(
"Chart of Accounts Importer", "All Accounts", file_name=self.import_file, for_validate=1
)
def validate_columns(data):
if not data:
frappe.throw(_('No data found. Seems like you uploaded a blank file'))
frappe.throw(_("No data found. Seems like you uploaded a blank file"))
no_of_columns = max([len(d) for d in data])
if no_of_columns > 7:
frappe.throw(_('More columns found than expected. Please compare the uploaded file with standard template'),
title=(_("Wrong Template")))
frappe.throw(
_("More columns found than expected. Please compare the uploaded file with standard template"),
title=(_("Wrong Template")),
)
@frappe.whitelist()
def validate_company(company):
parent_company, allow_account_creation_against_child_company = frappe.db.get_value('Company',
{'name': company}, ['parent_company',
'allow_account_creation_against_child_company'])
parent_company, allow_account_creation_against_child_company = frappe.db.get_value(
"Company", {"name": company}, ["parent_company", "allow_account_creation_against_child_company"]
)
if parent_company and (not allow_account_creation_against_child_company):
msg = _("{} is a child company.").format(frappe.bold(company)) + " "
msg += _("Please import accounts against parent company or enable {} in company master.").format(
frappe.bold('Allow Account Creation Against Child Company'))
frappe.throw(msg, title=_('Wrong Company'))
frappe.bold("Allow Account Creation Against Child Company")
)
frappe.throw(msg, title=_("Wrong Company"))
if frappe.db.get_all('GL Entry', {"company": company}, "name", limit=1):
if frappe.db.get_all("GL Entry", {"company": company}, "name", limit=1):
return False
@frappe.whitelist()
def import_coa(file_name, company):
# delete existing data for accounts
@@ -60,7 +68,7 @@ def import_coa(file_name, company):
# create accounts
file_doc, extension = get_file(file_name)
if extension == 'csv':
if extension == "csv":
data = generate_data_from_csv(file_doc)
else:
data = generate_data_from_excel(file_doc, extension)
@@ -72,27 +80,33 @@ def import_coa(file_name, company):
# trigger on_update for company to reset default accounts
set_default_accounts(company)
def get_file(file_name):
file_doc = frappe.get_doc("File", {"file_url": file_name})
parts = file_doc.get_extension()
extension = parts[1]
extension = extension.lstrip(".")
if extension not in ('csv', 'xlsx', 'xls'):
frappe.throw(_("Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload"))
if extension not in ("csv", "xlsx", "xls"):
frappe.throw(
_(
"Only CSV and Excel files can be used to for importing data. Please check the file format you are trying to upload"
)
)
return file_doc, extension
return file_doc, extension
def generate_data_from_csv(file_doc, as_dict=False):
''' read csv file and return the generated nested tree '''
"""read csv file and return the generated nested tree"""
file_path = file_doc.get_full_path()
data = []
with open(file_path, 'r') as in_file:
with open(file_path, "r") as in_file:
csv_reader = list(csv.reader(in_file))
headers = csv_reader[0]
del csv_reader[0] # delete top row and headers row
del csv_reader[0] # delete top row and headers row
for row in csv_reader:
if as_dict:
@@ -106,6 +120,7 @@ def generate_data_from_csv(file_doc, as_dict=False):
# convert csv data
return data
def generate_data_from_excel(file_doc, extension, as_dict=False):
content = file_doc.get_content()
@@ -123,20 +138,21 @@ def generate_data_from_excel(file_doc, extension, as_dict=False):
data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
else:
if not row[1]:
row[1] = row[0]
row[3] = row[2]
row[1] = row[0]
row[3] = row[2]
data.append(row)
return data
@frappe.whitelist()
def get_coa(doctype, parent, is_root=False, file_name=None, for_validate=0):
''' called by tree view (to fetch node's children) '''
"""called by tree view (to fetch node's children)"""
file_doc, extension = get_file(file_name)
parent = None if parent==_('All Accounts') else parent
parent = None if parent == _("All Accounts") else parent
if extension == 'csv':
if extension == "csv":
data = generate_data_from_csv(file_doc)
else:
data = generate_data_from_excel(file_doc, extension)
@@ -146,32 +162,33 @@ def get_coa(doctype, parent, is_root=False, file_name=None, for_validate=0):
if not for_validate:
forest = build_forest(data)
accounts = build_tree_from_json("", chart_data=forest, from_coa_importer=True) # returns a list of dict in a tree render-able form
accounts = build_tree_from_json(
"", chart_data=forest, from_coa_importer=True
) # returns a list of dict in a tree render-able form
# filter out to show data for the selected node only
accounts = [d for d in accounts if d['parent_account']==parent]
accounts = [d for d in accounts if d["parent_account"] == parent]
return accounts
else:
return {
'show_import_button': 1
}
return {"show_import_button": 1}
def build_forest(data):
'''
converts list of list into a nested tree
if a = [[1,1], [1,2], [3,2], [4,4], [5,4]]
tree = {
1: {
2: {
3: {}
}
},
4: {
5: {}
}
}
'''
"""
converts list of list into a nested tree
if a = [[1,1], [1,2], [3,2], [4,4], [5,4]]
tree = {
1: {
2: {
3: {}
}
},
4: {
5: {}
}
}
"""
# set the value of nested dictionary
def set_nested(d, path, value):
@@ -195,8 +212,11 @@ def build_forest(data):
elif account_name == child:
parent_account_list = return_parent(data, parent_account)
if not parent_account_list and parent_account:
frappe.throw(_("The parent account {0} does not exists in the uploaded template").format(
frappe.bold(parent_account)))
frappe.throw(
_("The parent account {0} does not exists in the uploaded template").format(
frappe.bold(parent_account)
)
)
return [child] + parent_account_list
charts_map, paths = {}, []
@@ -205,7 +225,15 @@ def build_forest(data):
error_messages = []
for i in data:
account_name, parent_account, account_number, parent_account_number, is_group, account_type, root_type = i
(
account_name,
parent_account,
account_number,
parent_account_number,
is_group,
account_type,
root_type,
) = i
if not account_name:
error_messages.append("Row {0}: Please enter Account Name".format(line_no))
@@ -216,13 +244,17 @@ def build_forest(data):
account_name = "{} - {}".format(account_number, account_name)
charts_map[account_name] = {}
charts_map[account_name]['account_name'] = name
if account_number: charts_map[account_name]["account_number"] = account_number
if cint(is_group) == 1: charts_map[account_name]["is_group"] = is_group
if account_type: charts_map[account_name]["account_type"] = account_type
if root_type: charts_map[account_name]["root_type"] = root_type
charts_map[account_name]["account_name"] = name
if account_number:
charts_map[account_name]["account_number"] = account_number
if cint(is_group) == 1:
charts_map[account_name]["is_group"] = is_group
if account_type:
charts_map[account_name]["account_type"] = account_type
if root_type:
charts_map[account_name]["root_type"] = root_type
path = return_parent(data, account_name)[::-1]
paths.append(path) # List of path is created
paths.append(path) # List of path is created
line_no += 1
if error_messages:
@@ -231,27 +263,32 @@ def build_forest(data):
out = {}
for path in paths:
for n, account_name in enumerate(path):
set_nested(out, path[:n+1], charts_map[account_name]) # setting the value of nested dictionary.
set_nested(
out, path[: n + 1], charts_map[account_name]
) # setting the value of nested dictionary.
return out
def build_response_as_excel(writer):
filename = frappe.generate_hash("", 10)
with open(filename, 'wb') as f:
f.write(cstr(writer.getvalue()).encode('utf-8'))
with open(filename, "wb") as f:
f.write(cstr(writer.getvalue()).encode("utf-8"))
f = open(filename)
reader = csv.reader(f)
from frappe.utils.xlsxutils import make_xlsx
xlsx_file = make_xlsx(reader, "Chart of Accounts Importer Template")
f.close()
os.remove(filename)
# write out response as a xlsx type
frappe.response['filename'] = 'coa_importer_template.xlsx'
frappe.response['filecontent'] = xlsx_file.getvalue()
frappe.response['type'] = 'binary'
frappe.response["filename"] = "coa_importer_template.xlsx"
frappe.response["filecontent"] = xlsx_file.getvalue()
frappe.response["type"] = "binary"
@frappe.whitelist()
def download_template(file_type, template_type):
@@ -259,34 +296,46 @@ def download_template(file_type, template_type):
writer = get_template(template_type)
if file_type == 'CSV':
if file_type == "CSV":
# download csv file
frappe.response['result'] = cstr(writer.getvalue())
frappe.response['type'] = 'csv'
frappe.response['doctype'] = 'Chart of Accounts Importer'
frappe.response["result"] = cstr(writer.getvalue())
frappe.response["type"] = "csv"
frappe.response["doctype"] = "Chart of Accounts Importer"
else:
build_response_as_excel(writer)
def get_template(template_type):
fields = ["Account Name", "Parent Account", "Account Number", "Parent Account Number", "Is Group", "Account Type", "Root Type"]
fields = [
"Account Name",
"Parent Account",
"Account Number",
"Parent Account Number",
"Is Group",
"Account Type",
"Root Type",
]
writer = UnicodeWriter()
writer.writerow(fields)
if template_type == 'Blank Template':
for root_type in get_root_types():
writer.writerow(['', '', '', 1, '', root_type])
if template_type == "Blank Template":
for root_type in get_root_types():
writer.writerow(["", "", "", 1, "", root_type])
for account in get_mandatory_group_accounts():
writer.writerow(['', '', '', 1, account, "Asset"])
writer.writerow(["", "", "", 1, account, "Asset"])
for account_type in get_mandatory_account_types():
writer.writerow(['', '', '', 0, account_type.get('account_type'), account_type.get('root_type')])
writer.writerow(
["", "", "", 0, account_type.get("account_type"), account_type.get("root_type")]
)
else:
writer = get_sample_template(writer)
return writer
def get_sample_template(writer):
template = [
["Application Of Funds(Assets)", "", "", "", 1, "", "Asset"],
@@ -316,7 +365,7 @@ def get_sample_template(writer):
@frappe.whitelist()
def validate_accounts(file_doc, extension):
if extension == 'csv':
if extension == "csv":
accounts = generate_data_from_csv(file_doc, as_dict=True)
else:
accounts = generate_data_from_excel(file_doc, extension, as_dict=True)
@@ -325,7 +374,9 @@ def validate_accounts(file_doc, extension):
for account in accounts:
accounts_dict.setdefault(account["account_name"], account)
if "parent_account" not in account:
msg = _("Please make sure the file you are using has 'Parent Account' column present in the header.")
msg = _(
"Please make sure the file you are using has 'Parent Account' column present in the header."
)
msg += "<br><br>"
msg += _("Alternatively, you can download the template and fill your data in.")
frappe.throw(msg, title=_("Parent Account Missing"))
@@ -336,77 +387,106 @@ def validate_accounts(file_doc, extension):
return [True, len(accounts)]
def validate_root(accounts):
roots = [accounts[d] for d in accounts if not accounts[d].get('parent_account')]
roots = [accounts[d] for d in accounts if not accounts[d].get("parent_account")]
error_messages = []
for account in roots:
if not account.get("root_type") and account.get("account_name"):
error_messages.append(_("Please enter Root Type for account- {0}").format(account.get("account_name")))
error_messages.append(
_("Please enter Root Type for account- {0}").format(account.get("account_name"))
)
elif account.get("root_type") not in get_root_types() and account.get("account_name"):
error_messages.append(_("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity").format(account.get("account_name")))
error_messages.append(
_("Root Type for {0} must be one of the Asset, Liability, Income, Expense and Equity").format(
account.get("account_name")
)
)
validate_missing_roots(roots)
if error_messages:
frappe.throw("<br>".join(error_messages))
def validate_missing_roots(roots):
root_types_added = set(d.get('root_type') for d in roots)
root_types_added = set(d.get("root_type") for d in roots)
missing = list(set(get_root_types()) - root_types_added)
if missing:
frappe.throw(_("Please add Root Account for - {0}").format(' , '.join(missing)))
frappe.throw(_("Please add Root Account for - {0}").format(" , ".join(missing)))
def get_root_types():
return ('Asset', 'Liability', 'Expense', 'Income', 'Equity')
return ("Asset", "Liability", "Expense", "Income", "Equity")
def get_report_type(root_type):
if root_type in ('Asset', 'Liability', 'Equity'):
return 'Balance Sheet'
if root_type in ("Asset", "Liability", "Equity"):
return "Balance Sheet"
else:
return 'Profit and Loss'
return "Profit and Loss"
def get_mandatory_group_accounts():
return ('Bank', 'Cash', 'Stock')
return ("Bank", "Cash", "Stock")
def get_mandatory_account_types():
return [
{'account_type': 'Cost of Goods Sold', 'root_type': 'Expense'},
{'account_type': 'Depreciation', 'root_type': 'Expense'},
{'account_type': 'Fixed Asset', 'root_type': 'Asset'},
{'account_type': 'Payable', 'root_type': 'Liability'},
{'account_type': 'Receivable', 'root_type': 'Asset'},
{'account_type': 'Stock Adjustment', 'root_type': 'Expense'},
{'account_type': 'Bank', 'root_type': 'Asset'},
{'account_type': 'Cash', 'root_type': 'Asset'},
{'account_type': 'Stock', 'root_type': 'Asset'}
{"account_type": "Cost of Goods Sold", "root_type": "Expense"},
{"account_type": "Depreciation", "root_type": "Expense"},
{"account_type": "Fixed Asset", "root_type": "Asset"},
{"account_type": "Payable", "root_type": "Liability"},
{"account_type": "Receivable", "root_type": "Asset"},
{"account_type": "Stock Adjustment", "root_type": "Expense"},
{"account_type": "Bank", "root_type": "Asset"},
{"account_type": "Cash", "root_type": "Asset"},
{"account_type": "Stock", "root_type": "Asset"},
]
def unset_existing_data(company):
linked = frappe.db.sql('''select fieldname from tabDocField
where fieldtype="Link" and options="Account" and parent="Company"''', as_dict=True)
linked = frappe.db.sql(
'''select fieldname from tabDocField
where fieldtype="Link" and options="Account" and parent="Company"''',
as_dict=True,
)
# remove accounts data from company
update_values = {d.fieldname: '' for d in linked}
frappe.db.set_value('Company', company, update_values, update_values)
update_values = {d.fieldname: "" for d in linked}
frappe.db.set_value("Company", company, update_values, update_values)
# remove accounts data from various doctypes
for doctype in ["Account", "Party Account", "Mode of Payment Account", "Tax Withholding Account",
"Sales Taxes and Charges Template", "Purchase Taxes and Charges Template"]:
frappe.db.sql('''delete from `tab{0}` where `company`="%s"''' # nosec
.format(doctype) % (company))
for doctype in [
"Account",
"Party Account",
"Mode of Payment Account",
"Tax Withholding Account",
"Sales Taxes and Charges Template",
"Purchase Taxes and Charges Template",
]:
frappe.db.sql(
'''delete from `tab{0}` where `company`="%s"'''.format(doctype) % (company) # nosec
)
def set_default_accounts(company):
from erpnext.setup.doctype.company.company import install_country_fixtures
company = frappe.get_doc('Company', company)
company.update({
"default_receivable_account": frappe.db.get_value("Account",
{"company": company.name, "account_type": "Receivable", "is_group": 0}),
"default_payable_account": frappe.db.get_value("Account",
{"company": company.name, "account_type": "Payable", "is_group": 0})
})
company = frappe.get_doc("Company", company)
company.update(
{
"default_receivable_account": frappe.db.get_value(
"Account", {"company": company.name, "account_type": "Receivable", "is_group": 0}
),
"default_payable_account": frappe.db.get_value(
"Account", {"company": company.name, "account_type": "Payable", "is_group": 0}
),
}
)
company.save()
install_country_fixtures(company.name, company.country)

View File

@@ -10,17 +10,20 @@ from frappe.model.document import Document
class ChequePrintTemplate(Document):
pass
@frappe.whitelist()
def create_or_update_cheque_print_format(template_name):
if not frappe.db.exists("Print Format", template_name):
cheque_print = frappe.new_doc("Print Format")
cheque_print.update({
"doc_type": "Payment Entry",
"standard": "No",
"custom_format": 1,
"print_format_type": "Jinja",
"name": template_name
})
cheque_print.update(
{
"doc_type": "Payment Entry",
"standard": "No",
"custom_format": 1,
"print_format_type": "Jinja",
"name": template_name,
}
)
else:
cheque_print = frappe.get_doc("Print Format", template_name)
@@ -69,10 +72,12 @@ def create_or_update_cheque_print_format(template_name):
{{doc.company}}
</span>
</div>
</div>"""%{
"starting_position_from_top_edge": doc.starting_position_from_top_edge \
if doc.cheque_size == "A4" else 0.0,
"cheque_width": doc.cheque_width, "cheque_height": doc.cheque_height,
</div>""" % {
"starting_position_from_top_edge": doc.starting_position_from_top_edge
if doc.cheque_size == "A4"
else 0.0,
"cheque_width": doc.cheque_width,
"cheque_height": doc.cheque_height,
"acc_pay_dist_from_top_edge": doc.acc_pay_dist_from_top_edge,
"acc_pay_dist_from_left_edge": doc.acc_pay_dist_from_left_edge,
"message_to_show": doc.message_to_show if doc.message_to_show else _("Account Pay Only"),
@@ -89,7 +94,7 @@ def create_or_update_cheque_print_format(template_name):
"amt_in_figures_from_top_edge": doc.amt_in_figures_from_top_edge,
"amt_in_figures_from_left_edge": doc.amt_in_figures_from_left_edge,
"signatory_from_top_edge": doc.signatory_from_top_edge,
"signatory_from_left_edge": doc.signatory_from_left_edge
"signatory_from_left_edge": doc.signatory_from_left_edge,
}
cheque_print.save(ignore_permissions=True)

View File

@@ -5,5 +5,6 @@ import unittest
# test_records = frappe.get_test_records('Cheque Print Template')
class TestChequePrintTemplate(unittest.TestCase):
pass

View File

@@ -11,11 +11,14 @@ from erpnext.accounts.utils import validate_field_number
class CostCenter(NestedSet):
nsm_parent_field = 'parent_cost_center'
nsm_parent_field = "parent_cost_center"
def autoname(self):
from erpnext.accounts.utils import get_autoname_with_number
self.name = get_autoname_with_number(self.cost_center_number, self.cost_center_name, None, self.company)
self.name = get_autoname_with_number(
self.cost_center_number, self.cost_center_name, None, self.company
)
def validate(self):
self.validate_mandatory()
@@ -27,15 +30,31 @@ class CostCenter(NestedSet):
if not self.distributed_cost_center:
frappe.throw(_("Please enter distributed cost center"))
if sum(x.percentage_allocation for x in self.distributed_cost_center) != 100:
frappe.throw(_("Total percentage allocation for distributed cost center should be equal to 100"))
if not self.get('__islocal'):
if not cint(frappe.get_cached_value("Cost Center", {"name": self.name}, "enable_distributed_cost_center")) \
and self.check_if_part_of_distributed_cost_center():
frappe.throw(_("Cannot enable Distributed Cost Center for a Cost Center already allocated in another Distributed Cost Center"))
frappe.throw(
_("Total percentage allocation for distributed cost center should be equal to 100")
)
if not self.get("__islocal"):
if (
not cint(
frappe.get_cached_value("Cost Center", {"name": self.name}, "enable_distributed_cost_center")
)
and self.check_if_part_of_distributed_cost_center()
):
frappe.throw(
_(
"Cannot enable Distributed Cost Center for a Cost Center already allocated in another Distributed Cost Center"
)
)
if next((True for x in self.distributed_cost_center if x.cost_center == x.parent), False):
frappe.throw(_("Parent Cost Center cannot be added in Distributed Cost Center"))
if check_if_distributed_cost_center_enabled(list(x.cost_center for x in self.distributed_cost_center)):
frappe.throw(_("A Distributed Cost Center cannot be added in the Distributed Cost Center allocation table."))
if check_if_distributed_cost_center_enabled(
list(x.cost_center for x in self.distributed_cost_center)
):
frappe.throw(
_(
"A Distributed Cost Center cannot be added in the Distributed Cost Center allocation table."
)
)
else:
self.distributed_cost_center = []
@@ -47,9 +66,12 @@ class CostCenter(NestedSet):
def validate_parent_cost_center(self):
if self.parent_cost_center:
if not frappe.db.get_value('Cost Center', self.parent_cost_center, 'is_group'):
frappe.throw(_("{0} is not a group node. Please select a group node as parent cost center").format(
frappe.bold(self.parent_cost_center)))
if not frappe.db.get_value("Cost Center", self.parent_cost_center, "is_group"):
frappe.throw(
_("{0} is not a group node. Please select a group node as parent cost center").format(
frappe.bold(self.parent_cost_center)
)
)
@frappe.whitelist()
def convert_group_to_ledger(self):
@@ -65,9 +87,13 @@ class CostCenter(NestedSet):
@frappe.whitelist()
def convert_ledger_to_group(self):
if cint(self.enable_distributed_cost_center):
frappe.throw(_("Cost Center with enabled distributed cost center can not be converted to group"))
frappe.throw(
_("Cost Center with enabled distributed cost center can not be converted to group")
)
if self.check_if_part_of_distributed_cost_center():
frappe.throw(_("Cost Center Already Allocated in a Distributed Cost Center cannot be converted to group"))
frappe.throw(
_("Cost Center Already Allocated in a Distributed Cost Center cannot be converted to group")
)
if self.check_gle_exists():
frappe.throw(_("Cost Center with existing transactions can not be converted to group"))
self.is_group = 1
@@ -78,8 +104,11 @@ class CostCenter(NestedSet):
return frappe.db.get_value("GL Entry", {"cost_center": self.name})
def check_if_child_exists(self):
return frappe.db.sql("select name from `tabCost Center` where \
parent_cost_center = %s and docstatus != 2", self.name)
return frappe.db.sql(
"select name from `tabCost Center` where \
parent_cost_center = %s and docstatus != 2",
self.name,
)
def check_if_part_of_distributed_cost_center(self):
return frappe.db.get_value("Distributed Cost Center", {"cost_center": self.name})
@@ -87,6 +116,7 @@ class CostCenter(NestedSet):
def before_rename(self, olddn, newdn, merge=False):
# Add company abbr if not provided
from erpnext.setup.doctype.company.company import get_name_with_abbr
new_cost_center = get_name_with_abbr(newdn, self.company)
# Validate properties before merging
@@ -100,7 +130,9 @@ class CostCenter(NestedSet):
super(CostCenter, self).after_rename(olddn, newdn, merge)
if not merge:
new_cost_center = frappe.db.get_value("Cost Center", newdn, ["cost_center_name", "cost_center_number"], as_dict=1)
new_cost_center = frappe.db.get_value(
"Cost Center", newdn, ["cost_center_name", "cost_center_number"], as_dict=1
)
# exclude company abbr
new_parts = newdn.split(" - ")[:-1]
@@ -109,7 +141,9 @@ class CostCenter(NestedSet):
if len(new_parts) == 1:
new_parts = newdn.split(" ")
if new_cost_center.cost_center_number != new_parts[0]:
validate_field_number("Cost Center", self.name, new_parts[0], self.company, "cost_center_number")
validate_field_number(
"Cost Center", self.name, new_parts[0], self.company, "cost_center_number"
)
self.cost_center_number = new_parts[0]
self.db_set("cost_center_number", new_parts[0])
new_parts = new_parts[1:]
@@ -120,14 +154,19 @@ class CostCenter(NestedSet):
self.cost_center_name = cost_center_name
self.db_set("cost_center_name", cost_center_name)
def on_doctype_update():
frappe.db.add_index("Cost Center", ["lft", "rgt"])
def get_name_with_number(new_account, account_number):
if account_number and not new_account[0].isdigit():
new_account = account_number + " - " + new_account
return new_account
def check_if_distributed_cost_center_enabled(cost_center_list):
value_list = frappe.get_list("Cost Center", {"name": ["in", cost_center_list]}, "enable_distributed_cost_center", as_list=1)
value_list = frappe.get_list(
"Cost Center", {"name": ["in", cost_center_list]}, "enable_distributed_cost_center", as_list=1
)
return next((True for x in value_list if x[0]), False)

View File

@@ -1,14 +1,8 @@
from frappe import _
def get_data():
return {
'fieldname': 'cost_center',
'reports': [
{
'label': _('Reports'),
'items': ['Budget Variance Report', 'General Ledger']
}
]
"fieldname": "cost_center",
"reports": [{"label": _("Reports"), "items": ["Budget Variance Report", "General Ledger"]}],
}

View File

@@ -5,51 +5,53 @@ import unittest
import frappe
test_records = frappe.get_test_records('Cost Center')
test_records = frappe.get_test_records("Cost Center")
class TestCostCenter(unittest.TestCase):
def test_cost_center_creation_against_child_node(self):
if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center 2 - _TC'}):
if not frappe.db.get_value("Cost Center", {"name": "_Test Cost Center 2 - _TC"}):
frappe.get_doc(test_records[1]).insert()
cost_center = frappe.get_doc({
'doctype': 'Cost Center',
'cost_center_name': '_Test Cost Center 3',
'parent_cost_center': '_Test Cost Center 2 - _TC',
'is_group': 0,
'company': '_Test Company'
})
cost_center = frappe.get_doc(
{
"doctype": "Cost Center",
"cost_center_name": "_Test Cost Center 3",
"parent_cost_center": "_Test Cost Center 2 - _TC",
"is_group": 0,
"company": "_Test Company",
}
)
self.assertRaises(frappe.ValidationError, cost_center.save)
def test_validate_distributed_cost_center(self):
if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center - _TC'}):
if not frappe.db.get_value("Cost Center", {"name": "_Test Cost Center - _TC"}):
frappe.get_doc(test_records[0]).insert()
if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center 2 - _TC'}):
if not frappe.db.get_value("Cost Center", {"name": "_Test Cost Center 2 - _TC"}):
frappe.get_doc(test_records[1]).insert()
invalid_distributed_cost_center = frappe.get_doc({
"company": "_Test Company",
"cost_center_name": "_Test Distributed Cost Center",
"doctype": "Cost Center",
"is_group": 0,
"parent_cost_center": "_Test Company - _TC",
"enable_distributed_cost_center": 1,
"distributed_cost_center": [{
"cost_center": "_Test Cost Center - _TC",
"percentage_allocation": 40
}, {
"cost_center": "_Test Cost Center 2 - _TC",
"percentage_allocation": 50
}
]
})
invalid_distributed_cost_center = frappe.get_doc(
{
"company": "_Test Company",
"cost_center_name": "_Test Distributed Cost Center",
"doctype": "Cost Center",
"is_group": 0,
"parent_cost_center": "_Test Company - _TC",
"enable_distributed_cost_center": 1,
"distributed_cost_center": [
{"cost_center": "_Test Cost Center - _TC", "percentage_allocation": 40},
{"cost_center": "_Test Cost Center 2 - _TC", "percentage_allocation": 50},
],
}
)
self.assertRaises(frappe.ValidationError, invalid_distributed_cost_center.save)
def create_cost_center(**args):
args = frappe._dict(args)
if args.cost_center_name:

View File

@@ -15,7 +15,7 @@ class CouponCode(Document):
if not self.coupon_code:
if self.coupon_type == "Promotional":
self.coupon_code =''.join(i for i in self.coupon_name if not i.isdigit())[0:8].upper()
self.coupon_code = "".join(i for i in self.coupon_name if not i.isdigit())[0:8].upper()
elif self.coupon_type == "Gift Card":
self.coupon_code = frappe.generate_hash()[:10].upper()

View File

@@ -7,92 +7,110 @@ import frappe
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
test_dependencies = ['Item']
test_dependencies = ["Item"]
def test_create_test_data():
frappe.set_user("Administrator")
# create test item
if not frappe.db.exists("Item","_Test Tesla Car"):
item = frappe.get_doc({
"description": "_Test Tesla Car",
"doctype": "Item",
"has_batch_no": 0,
"has_serial_no": 0,
"inspection_required": 0,
"is_stock_item": 1,
"opening_stock":100,
"is_sub_contracted_item": 0,
"item_code": "_Test Tesla Car",
"item_group": "_Test Item Group",
"item_name": "_Test Tesla Car",
"apply_warehouse_wise_reorder_level": 0,
"warehouse":"Stores - _TC",
"gst_hsn_code": "999800",
"valuation_rate": 5000,
"standard_rate":5000,
"item_defaults": [{
"company": "_Test Company",
"default_warehouse": "Stores - _TC",
"default_price_list":"_Test Price List",
"expense_account": "Cost of Goods Sold - _TC",
"buying_cost_center": "Main - _TC",
"selling_cost_center": "Main - _TC",
"income_account": "Sales - _TC"
}],
})
if not frappe.db.exists("Item", "_Test Tesla Car"):
item = frappe.get_doc(
{
"description": "_Test Tesla Car",
"doctype": "Item",
"has_batch_no": 0,
"has_serial_no": 0,
"inspection_required": 0,
"is_stock_item": 1,
"opening_stock": 100,
"is_sub_contracted_item": 0,
"item_code": "_Test Tesla Car",
"item_group": "_Test Item Group",
"item_name": "_Test Tesla Car",
"apply_warehouse_wise_reorder_level": 0,
"warehouse": "Stores - _TC",
"gst_hsn_code": "999800",
"valuation_rate": 5000,
"standard_rate": 5000,
"item_defaults": [
{
"company": "_Test Company",
"default_warehouse": "Stores - _TC",
"default_price_list": "_Test Price List",
"expense_account": "Cost of Goods Sold - _TC",
"buying_cost_center": "Main - _TC",
"selling_cost_center": "Main - _TC",
"income_account": "Sales - _TC",
}
],
}
)
item.insert()
# create test item price
item_price = frappe.get_list('Item Price', filters={'item_code': '_Test Tesla Car', 'price_list': '_Test Price List'}, fields=['name'])
if len(item_price)==0:
item_price = frappe.get_doc({
"doctype": "Item Price",
"item_code": "_Test Tesla Car",
"price_list": "_Test Price List",
"price_list_rate": 5000
})
item_price = frappe.get_list(
"Item Price",
filters={"item_code": "_Test Tesla Car", "price_list": "_Test Price List"},
fields=["name"],
)
if len(item_price) == 0:
item_price = frappe.get_doc(
{
"doctype": "Item Price",
"item_code": "_Test Tesla Car",
"price_list": "_Test Price List",
"price_list_rate": 5000,
}
)
item_price.insert()
# create test item pricing rule
if not frappe.db.exists("Pricing Rule", {"title": "_Test Pricing Rule for _Test Item"}):
item_pricing_rule = frappe.get_doc({
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule for _Test Item",
"apply_on": "Item Code",
"items": [{
"item_code": "_Test Tesla Car"
}],
"warehouse":"Stores - _TC",
"coupon_code_based":1,
"selling": 1,
"rate_or_discount": "Discount Percentage",
"discount_percentage": 30,
"company": "_Test Company",
"currency":"INR",
"for_price_list":"_Test Price List"
})
item_pricing_rule = frappe.get_doc(
{
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule for _Test Item",
"apply_on": "Item Code",
"items": [{"item_code": "_Test Tesla Car"}],
"warehouse": "Stores - _TC",
"coupon_code_based": 1,
"selling": 1,
"rate_or_discount": "Discount Percentage",
"discount_percentage": 30,
"company": "_Test Company",
"currency": "INR",
"for_price_list": "_Test Price List",
}
)
item_pricing_rule.insert()
# create test item sales partner
if not frappe.db.exists("Sales Partner","_Test Coupon Partner"):
sales_partner = frappe.get_doc({
"doctype": "Sales Partner",
"partner_name":"_Test Coupon Partner",
"commission_rate":2,
"referral_code": "COPART"
})
if not frappe.db.exists("Sales Partner", "_Test Coupon Partner"):
sales_partner = frappe.get_doc(
{
"doctype": "Sales Partner",
"partner_name": "_Test Coupon Partner",
"commission_rate": 2,
"referral_code": "COPART",
}
)
sales_partner.insert()
# create test item coupon code
if not frappe.db.exists("Coupon Code", "SAVE30"):
pricing_rule = frappe.db.get_value("Pricing Rule", {"title": "_Test Pricing Rule for _Test Item"}, ['name'])
coupon_code = frappe.get_doc({
"doctype": "Coupon Code",
"coupon_name":"SAVE30",
"coupon_code":"SAVE30",
"pricing_rule": pricing_rule,
"valid_from": "2014-01-01",
"maximum_use":1,
"used":0
})
pricing_rule = frappe.db.get_value(
"Pricing Rule", {"title": "_Test Pricing Rule for _Test Item"}, ["name"]
)
coupon_code = frappe.get_doc(
{
"doctype": "Coupon Code",
"coupon_name": "SAVE30",
"coupon_code": "SAVE30",
"pricing_rule": pricing_rule,
"valid_from": "2014-01-01",
"maximum_use": 1,
"used": 0,
}
)
coupon_code.insert()
class TestCouponCode(unittest.TestCase):
def setUp(self):
test_create_test_data()
@@ -103,15 +121,21 @@ class TestCouponCode(unittest.TestCase):
def test_sales_order_with_coupon_code(self):
frappe.db.set_value("Coupon Code", "SAVE30", "used", 0)
so = make_sales_order(company='_Test Company', warehouse='Stores - _TC',
customer="_Test Customer", selling_price_list="_Test Price List",
item_code="_Test Tesla Car", rate=5000, qty=1,
do_not_submit=True)
so = make_sales_order(
company="_Test Company",
warehouse="Stores - _TC",
customer="_Test Customer",
selling_price_list="_Test Price List",
item_code="_Test Tesla Car",
rate=5000,
qty=1,
do_not_submit=True,
)
self.assertEqual(so.items[0].rate, 5000)
so.coupon_code='SAVE30'
so.sales_partner='_Test Coupon Partner'
so.coupon_code = "SAVE30"
so.sales_partner = "_Test Coupon Partner"
so.save()
# check item price after coupon code is applied

View File

@@ -20,78 +20,99 @@ class Dunning(AccountsController):
self.validate_overdue_days()
self.validate_amount()
if not self.income_account:
self.income_account = frappe.db.get_value('Company', self.company, 'default_income_account')
self.income_account = frappe.db.get_value("Company", self.company, "default_income_account")
def validate_overdue_days(self):
self.overdue_days = (getdate(self.posting_date) - getdate(self.due_date)).days or 0
def validate_amount(self):
amounts = calculate_interest_and_amount(
self.outstanding_amount, self.rate_of_interest, self.dunning_fee, self.overdue_days)
if self.interest_amount != amounts.get('interest_amount'):
self.interest_amount = flt(amounts.get('interest_amount'), self.precision('interest_amount'))
if self.dunning_amount != amounts.get('dunning_amount'):
self.dunning_amount = flt(amounts.get('dunning_amount'), self.precision('dunning_amount'))
if self.grand_total != amounts.get('grand_total'):
self.grand_total = flt(amounts.get('grand_total'), self.precision('grand_total'))
self.outstanding_amount, self.rate_of_interest, self.dunning_fee, self.overdue_days
)
if self.interest_amount != amounts.get("interest_amount"):
self.interest_amount = flt(amounts.get("interest_amount"), self.precision("interest_amount"))
if self.dunning_amount != amounts.get("dunning_amount"):
self.dunning_amount = flt(amounts.get("dunning_amount"), self.precision("dunning_amount"))
if self.grand_total != amounts.get("grand_total"):
self.grand_total = flt(amounts.get("grand_total"), self.precision("grand_total"))
def on_submit(self):
self.make_gl_entries()
def on_cancel(self):
if self.dunning_amount:
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def make_gl_entries(self):
if not self.dunning_amount:
return
gl_entries = []
invoice_fields = ["project", "cost_center", "debit_to", "party_account_currency", "conversion_rate", "cost_center"]
invoice_fields = [
"project",
"cost_center",
"debit_to",
"party_account_currency",
"conversion_rate",
"cost_center",
]
inv = frappe.db.get_value("Sales Invoice", self.sales_invoice, invoice_fields, as_dict=1)
accounting_dimensions = get_accounting_dimensions()
invoice_fields.extend(accounting_dimensions)
dunning_in_company_currency = flt(self.dunning_amount * inv.conversion_rate)
default_cost_center = frappe.get_cached_value('Company', self.company, 'cost_center')
default_cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
gl_entries.append(
self.get_gl_dict({
"account": inv.debit_to,
"party_type": "Customer",
"party": self.customer,
"due_date": self.due_date,
"against": self.income_account,
"debit": dunning_in_company_currency,
"debit_in_account_currency": self.dunning_amount,
"against_voucher": self.name,
"against_voucher_type": "Dunning",
"cost_center": inv.cost_center or default_cost_center,
"project": inv.project
}, inv.party_account_currency, item=inv)
self.get_gl_dict(
{
"account": inv.debit_to,
"party_type": "Customer",
"party": self.customer,
"due_date": self.due_date,
"against": self.income_account,
"debit": dunning_in_company_currency,
"debit_in_account_currency": self.dunning_amount,
"against_voucher": self.name,
"against_voucher_type": "Dunning",
"cost_center": inv.cost_center or default_cost_center,
"project": inv.project,
},
inv.party_account_currency,
item=inv,
)
)
gl_entries.append(
self.get_gl_dict({
"account": self.income_account,
"against": self.customer,
"credit": dunning_in_company_currency,
"cost_center": inv.cost_center or default_cost_center,
"credit_in_account_currency": self.dunning_amount,
"project": inv.project
}, item=inv)
self.get_gl_dict(
{
"account": self.income_account,
"against": self.customer,
"credit": dunning_in_company_currency,
"cost_center": inv.cost_center or default_cost_center,
"credit_in_account_currency": self.dunning_amount,
"project": inv.project,
},
item=inv,
)
)
make_gl_entries(
gl_entries, cancel=(self.docstatus == 2), update_outstanding="No", merge_entries=False
)
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding="No", merge_entries=False)
def resolve_dunning(doc, state):
for reference in doc.references:
if reference.reference_doctype == 'Sales Invoice' and reference.outstanding_amount <= 0:
dunnings = frappe.get_list('Dunning', filters={
'sales_invoice': reference.reference_name, 'status': ('!=', 'Resolved')}, ignore_permissions=True)
if reference.reference_doctype == "Sales Invoice" and reference.outstanding_amount <= 0:
dunnings = frappe.get_list(
"Dunning",
filters={"sales_invoice": reference.reference_name, "status": ("!=", "Resolved")},
ignore_permissions=True,
)
for dunning in dunnings:
frappe.db.set_value("Dunning", dunning.name, "status", 'Resolved')
frappe.db.set_value("Dunning", dunning.name, "status", "Resolved")
def calculate_interest_and_amount(outstanding_amount, rate_of_interest, dunning_fee, overdue_days):
interest_amount = 0
@@ -102,23 +123,26 @@ def calculate_interest_and_amount(outstanding_amount, rate_of_interest, dunning_
grand_total += flt(interest_amount)
dunning_amount = flt(interest_amount) + flt(dunning_fee)
return {
'interest_amount': interest_amount,
'grand_total': grand_total,
'dunning_amount': dunning_amount}
"interest_amount": interest_amount,
"grand_total": grand_total,
"dunning_amount": dunning_amount,
}
@frappe.whitelist()
def get_dunning_letter_text(dunning_type, doc, language=None):
if isinstance(doc, string_types):
doc = json.loads(doc)
if language:
filters = {'parent': dunning_type, 'language': language}
filters = {"parent": dunning_type, "language": language}
else:
filters = {'parent': dunning_type, 'is_default_language': 1}
letter_text = frappe.db.get_value('Dunning Letter Text', filters,
['body_text', 'closing_text', 'language'], as_dict=1)
filters = {"parent": dunning_type, "is_default_language": 1}
letter_text = frappe.db.get_value(
"Dunning Letter Text", filters, ["body_text", "closing_text", "language"], as_dict=1
)
if letter_text:
return {
'body_text': frappe.render_template(letter_text.body_text, doc),
'closing_text': frappe.render_template(letter_text.closing_text, doc),
'language': letter_text.language
"body_text": frappe.render_template(letter_text.body_text, doc),
"closing_text": frappe.render_template(letter_text.closing_text, doc),
"language": letter_text.language,
}

View File

@@ -1,18 +1,12 @@
from frappe import _
def get_data():
return {
'fieldname': 'dunning',
'non_standard_fieldnames': {
'Journal Entry': 'reference_name',
'Payment Entry': 'reference_name'
"fieldname": "dunning",
"non_standard_fieldnames": {
"Journal Entry": "reference_name",
"Payment Entry": "reference_name",
},
'transactions': [
{
'label': _('Payment'),
'items': ['Payment Entry', 'Journal Entry']
}
]
"transactions": [{"label": _("Payment"), "items": ["Payment Entry", "Journal Entry"]}],
}

View File

@@ -30,31 +30,35 @@ class TestDunning(unittest.TestCase):
def test_dunning(self):
dunning = create_dunning()
amounts = calculate_interest_and_amount(
dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days)
self.assertEqual(round(amounts.get('interest_amount'), 2), 0.44)
self.assertEqual(round(amounts.get('dunning_amount'), 2), 20.44)
self.assertEqual(round(amounts.get('grand_total'), 2), 120.44)
dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days
)
self.assertEqual(round(amounts.get("interest_amount"), 2), 0.44)
self.assertEqual(round(amounts.get("dunning_amount"), 2), 20.44)
self.assertEqual(round(amounts.get("grand_total"), 2), 120.44)
def test_dunning_with_zero_interest_rate(self):
dunning = create_dunning_with_zero_interest_rate()
amounts = calculate_interest_and_amount(
dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days)
self.assertEqual(round(amounts.get('interest_amount'), 2), 0)
self.assertEqual(round(amounts.get('dunning_amount'), 2), 20)
self.assertEqual(round(amounts.get('grand_total'), 2), 120)
dunning.outstanding_amount, dunning.rate_of_interest, dunning.dunning_fee, dunning.overdue_days
)
self.assertEqual(round(amounts.get("interest_amount"), 2), 0)
self.assertEqual(round(amounts.get("dunning_amount"), 2), 20)
self.assertEqual(round(amounts.get("grand_total"), 2), 120)
def test_gl_entries(self):
dunning = create_dunning()
dunning.submit()
gl_entries = frappe.db.sql("""select account, debit, credit
gl_entries = frappe.db.sql(
"""select account, debit, credit
from `tabGL Entry` where voucher_type='Dunning' and voucher_no=%s
order by account asc""", dunning.name, as_dict=1)
order by account asc""",
dunning.name,
as_dict=1,
)
self.assertTrue(gl_entries)
expected_values = dict((d[0], d) for d in [
['Debtors - _TC', 20.44, 0.0],
['Sales - _TC', 0.0, 20.44]
])
expected_values = dict(
(d[0], d) for d in [["Debtors - _TC", 20.44, 0.0], ["Sales - _TC", 0.0, 20.44]]
)
for gle in gl_entries:
self.assertEqual(expected_values[gle.account][0], gle.account)
self.assertEqual(expected_values[gle.account][1], gle.debit)
@@ -72,7 +76,7 @@ class TestDunning(unittest.TestCase):
pe.target_exchange_rate = 1
pe.insert()
pe.submit()
si_doc = frappe.get_doc('Sales Invoice', dunning.sales_invoice)
si_doc = frappe.get_doc("Sales Invoice", dunning.sales_invoice)
self.assertEqual(si_doc.outstanding_amount, 0)
@@ -80,8 +84,9 @@ def create_dunning():
posting_date = add_days(today(), -20)
due_date = add_days(today(), -15)
sales_invoice = create_sales_invoice_against_cost_center(
posting_date=posting_date, due_date=due_date, status='Overdue')
dunning_type = frappe.get_doc("Dunning Type", 'First Notice')
posting_date=posting_date, due_date=due_date, status="Overdue"
)
dunning_type = frappe.get_doc("Dunning Type", "First Notice")
dunning = frappe.new_doc("Dunning")
dunning.sales_invoice = sales_invoice.name
dunning.customer_name = sales_invoice.customer_name
@@ -91,18 +96,20 @@ def create_dunning():
dunning.company = sales_invoice.company
dunning.posting_date = nowdate()
dunning.due_date = sales_invoice.due_date
dunning.dunning_type = 'First Notice'
dunning.dunning_type = "First Notice"
dunning.rate_of_interest = dunning_type.rate_of_interest
dunning.dunning_fee = dunning_type.dunning_fee
dunning.save()
return dunning
def create_dunning_with_zero_interest_rate():
posting_date = add_days(today(), -20)
due_date = add_days(today(), -15)
sales_invoice = create_sales_invoice_against_cost_center(
posting_date=posting_date, due_date=due_date, status='Overdue')
dunning_type = frappe.get_doc("Dunning Type", 'First Notice with 0% Rate of Interest')
posting_date=posting_date, due_date=due_date, status="Overdue"
)
dunning_type = frappe.get_doc("Dunning Type", "First Notice with 0% Rate of Interest")
dunning = frappe.new_doc("Dunning")
dunning.sales_invoice = sales_invoice.name
dunning.customer_name = sales_invoice.customer_name
@@ -112,40 +119,44 @@ def create_dunning_with_zero_interest_rate():
dunning.company = sales_invoice.company
dunning.posting_date = nowdate()
dunning.due_date = sales_invoice.due_date
dunning.dunning_type = 'First Notice with 0% Rate of Interest'
dunning.dunning_type = "First Notice with 0% Rate of Interest"
dunning.rate_of_interest = dunning_type.rate_of_interest
dunning.dunning_fee = dunning_type.dunning_fee
dunning.save()
return dunning
def create_dunning_type():
dunning_type = frappe.new_doc("Dunning Type")
dunning_type.dunning_type = 'First Notice'
dunning_type.dunning_type = "First Notice"
dunning_type.start_day = 10
dunning_type.end_day = 20
dunning_type.dunning_fee = 20
dunning_type.rate_of_interest = 8
dunning_type.append(
"dunning_letter_text", {
'language': 'en',
'body_text': 'We have still not received payment for our invoice ',
'closing_text': 'We kindly request that you pay the outstanding amount immediately, including interest and late fees.'
}
"dunning_letter_text",
{
"language": "en",
"body_text": "We have still not received payment for our invoice ",
"closing_text": "We kindly request that you pay the outstanding amount immediately, including interest and late fees.",
},
)
dunning_type.save()
def create_dunning_type_with_zero_interest_rate():
dunning_type = frappe.new_doc("Dunning Type")
dunning_type.dunning_type = 'First Notice with 0% Rate of Interest'
dunning_type.dunning_type = "First Notice with 0% Rate of Interest"
dunning_type.start_day = 10
dunning_type.end_day = 20
dunning_type.dunning_fee = 20
dunning_type.rate_of_interest = 0
dunning_type.append(
"dunning_letter_text", {
'language': 'en',
'body_text': 'We have still not received payment for our invoice ',
'closing_text': 'We kindly request that you pay the outstanding amount immediately, and late fees.'
}
"dunning_letter_text",
{
"language": "en",
"body_text": "We have still not received payment for our invoice ",
"closing_text": "We kindly request that you pay the outstanding amount immediately, and late fees.",
},
)
dunning_type.save()

View File

@@ -20,8 +20,9 @@ class ExchangeRateRevaluation(Document):
def set_total_gain_loss(self):
total_gain_loss = 0
for d in self.accounts:
d.gain_loss = flt(d.new_balance_in_base_currency, d.precision("new_balance_in_base_currency")) \
- flt(d.balance_in_base_currency, d.precision("balance_in_base_currency"))
d.gain_loss = flt(
d.new_balance_in_base_currency, d.precision("new_balance_in_base_currency")
) - flt(d.balance_in_base_currency, d.precision("balance_in_base_currency"))
total_gain_loss += flt(d.gain_loss, d.precision("gain_loss"))
self.total_gain_loss = flt(total_gain_loss, self.precision("total_gain_loss"))
@@ -30,15 +31,15 @@ class ExchangeRateRevaluation(Document):
frappe.throw(_("Please select Company and Posting Date to getting entries"))
def on_cancel(self):
self.ignore_linked_doctypes = ('GL Entry')
self.ignore_linked_doctypes = "GL Entry"
@frappe.whitelist()
def check_journal_entry_condition(self):
total_debit = frappe.db.get_value("Journal Entry Account", {
'reference_type': 'Exchange Rate Revaluation',
'reference_name': self.name,
'docstatus': 1
}, "sum(debit) as sum")
total_debit = frappe.db.get_value(
"Journal Entry Account",
{"reference_type": "Exchange Rate Revaluation", "reference_name": self.name, "docstatus": 1},
"sum(debit) as sum",
)
total_amt = 0
for d in self.accounts:
@@ -54,28 +55,33 @@ class ExchangeRateRevaluation(Document):
accounts = []
self.validate_mandatory()
company_currency = erpnext.get_company_currency(self.company)
precision = get_field_precision(frappe.get_meta("Exchange Rate Revaluation Account")
.get_field("new_balance_in_base_currency"), company_currency)
precision = get_field_precision(
frappe.get_meta("Exchange Rate Revaluation Account").get_field("new_balance_in_base_currency"),
company_currency,
)
account_details = self.get_accounts_from_gle()
for d in account_details:
current_exchange_rate = d.balance / d.balance_in_account_currency \
if d.balance_in_account_currency else 0
current_exchange_rate = (
d.balance / d.balance_in_account_currency if d.balance_in_account_currency else 0
)
new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, self.posting_date)
new_balance_in_base_currency = flt(d.balance_in_account_currency * new_exchange_rate)
gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision)
if gain_loss:
accounts.append({
"account": d.account,
"party_type": d.party_type,
"party": d.party,
"account_currency": d.account_currency,
"balance_in_base_currency": d.balance,
"balance_in_account_currency": d.balance_in_account_currency,
"current_exchange_rate": current_exchange_rate,
"new_exchange_rate": new_exchange_rate,
"new_balance_in_base_currency": new_balance_in_base_currency
})
accounts.append(
{
"account": d.account,
"party_type": d.party_type,
"party": d.party,
"account_currency": d.account_currency,
"balance_in_base_currency": d.balance,
"balance_in_account_currency": d.balance_in_account_currency,
"current_exchange_rate": current_exchange_rate,
"new_exchange_rate": new_exchange_rate,
"new_balance_in_base_currency": new_balance_in_base_currency,
}
)
if not accounts:
self.throw_invalid_response_message(account_details)
@@ -84,7 +90,8 @@ class ExchangeRateRevaluation(Document):
def get_accounts_from_gle(self):
company_currency = erpnext.get_company_currency(self.company)
accounts = frappe.db.sql_list("""
accounts = frappe.db.sql_list(
"""
select name
from tabAccount
where is_group = 0
@@ -93,11 +100,14 @@ class ExchangeRateRevaluation(Document):
and account_type != 'Stock'
and company=%s
and account_currency != %s
order by name""",(self.company, company_currency))
order by name""",
(self.company, company_currency),
)
account_details = []
if accounts:
account_details = frappe.db.sql("""
account_details = frappe.db.sql(
"""
select
account, party_type, party, account_currency,
sum(debit_in_account_currency) - sum(credit_in_account_currency) as balance_in_account_currency,
@@ -109,7 +119,11 @@ class ExchangeRateRevaluation(Document):
group by account, NULLIF(party_type,''), NULLIF(party,'')
having sum(debit) != sum(credit)
order by account
""" % (', '.join(['%s']*len(accounts)), '%s'), tuple(accounts + [self.posting_date]), as_dict=1)
"""
% (", ".join(["%s"] * len(accounts)), "%s"),
tuple(accounts + [self.posting_date]),
as_dict=1,
)
return account_details
@@ -125,77 +139,107 @@ class ExchangeRateRevaluation(Document):
if self.total_gain_loss == 0:
return
unrealized_exchange_gain_loss_account = frappe.get_cached_value('Company', self.company,
"unrealized_exchange_gain_loss_account")
unrealized_exchange_gain_loss_account = frappe.get_cached_value(
"Company", self.company, "unrealized_exchange_gain_loss_account"
)
if not unrealized_exchange_gain_loss_account:
frappe.throw(_("Please set Unrealized Exchange Gain/Loss Account in Company {0}")
.format(self.company))
frappe.throw(
_("Please set Unrealized Exchange Gain/Loss Account in Company {0}").format(self.company)
)
journal_entry = frappe.new_doc('Journal Entry')
journal_entry.voucher_type = 'Exchange Rate Revaluation'
journal_entry = frappe.new_doc("Journal Entry")
journal_entry.voucher_type = "Exchange Rate Revaluation"
journal_entry.company = self.company
journal_entry.posting_date = self.posting_date
journal_entry.multi_currency = 1
journal_entry_accounts = []
for d in self.accounts:
dr_or_cr = "debit_in_account_currency" \
if d.get("balance_in_account_currency") > 0 else "credit_in_account_currency"
dr_or_cr = (
"debit_in_account_currency"
if d.get("balance_in_account_currency") > 0
else "credit_in_account_currency"
)
reverse_dr_or_cr = "debit_in_account_currency" \
if dr_or_cr=="credit_in_account_currency" else "credit_in_account_currency"
reverse_dr_or_cr = (
"debit_in_account_currency"
if dr_or_cr == "credit_in_account_currency"
else "credit_in_account_currency"
)
journal_entry_accounts.append({
"account": d.get("account"),
"party_type": d.get("party_type"),
"party": d.get("party"),
"account_currency": d.get("account_currency"),
"balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")),
dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")),
"exchange_rate": flt(d.get("new_exchange_rate"), d.precision("new_exchange_rate")),
journal_entry_accounts.append(
{
"account": d.get("account"),
"party_type": d.get("party_type"),
"party": d.get("party"),
"account_currency": d.get("account_currency"),
"balance": flt(
d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")
),
dr_or_cr: flt(
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
),
"exchange_rate": flt(d.get("new_exchange_rate"), d.precision("new_exchange_rate")),
"reference_type": "Exchange Rate Revaluation",
"reference_name": self.name,
}
)
journal_entry_accounts.append(
{
"account": d.get("account"),
"party_type": d.get("party_type"),
"party": d.get("party"),
"account_currency": d.get("account_currency"),
"balance": flt(
d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")
),
reverse_dr_or_cr: flt(
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
),
"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
"reference_type": "Exchange Rate Revaluation",
"reference_name": self.name,
}
)
journal_entry_accounts.append(
{
"account": unrealized_exchange_gain_loss_account,
"balance": get_balance_on(unrealized_exchange_gain_loss_account),
"debit_in_account_currency": abs(self.total_gain_loss) if self.total_gain_loss < 0 else 0,
"credit_in_account_currency": self.total_gain_loss if self.total_gain_loss > 0 else 0,
"exchange_rate": 1,
"reference_type": "Exchange Rate Revaluation",
"reference_name": self.name,
})
journal_entry_accounts.append({
"account": d.get("account"),
"party_type": d.get("party_type"),
"party": d.get("party"),
"account_currency": d.get("account_currency"),
"balance": flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")),
reverse_dr_or_cr: flt(abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")),
"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
"reference_type": "Exchange Rate Revaluation",
"reference_name": self.name
})
journal_entry_accounts.append({
"account": unrealized_exchange_gain_loss_account,
"balance": get_balance_on(unrealized_exchange_gain_loss_account),
"debit_in_account_currency": abs(self.total_gain_loss) if self.total_gain_loss < 0 else 0,
"credit_in_account_currency": self.total_gain_loss if self.total_gain_loss > 0 else 0,
"exchange_rate": 1,
"reference_type": "Exchange Rate Revaluation",
"reference_name": self.name,
})
}
)
journal_entry.set("accounts", journal_entry_accounts)
journal_entry.set_amounts_in_company_currency()
journal_entry.set_total_debit_credit()
return journal_entry.as_dict()
@frappe.whitelist()
def get_account_details(account, company, posting_date, party_type=None, party=None):
account_currency, account_type = frappe.db.get_value("Account", account,
["account_currency", "account_type"])
account_currency, account_type = frappe.db.get_value(
"Account", account, ["account_currency", "account_type"]
)
if account_type in ["Receivable", "Payable"] and not (party_type and party):
frappe.throw(_("Party Type and Party is mandatory for {0} account").format(account_type))
account_details = {}
company_currency = erpnext.get_company_currency(company)
balance = get_balance_on(account, date=posting_date, party_type=party_type, party=party, in_account_currency=False)
balance = get_balance_on(
account, date=posting_date, party_type=party_type, party=party, in_account_currency=False
)
if balance:
balance_in_account_currency = get_balance_on(account, date=posting_date, party_type=party_type, party=party)
current_exchange_rate = balance / balance_in_account_currency if balance_in_account_currency else 0
balance_in_account_currency = get_balance_on(
account, date=posting_date, party_type=party_type, party=party
)
current_exchange_rate = (
balance / balance_in_account_currency if balance_in_account_currency else 0
)
new_exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
new_balance_in_base_currency = balance_in_account_currency * new_exchange_rate
account_details = {
@@ -204,7 +248,7 @@ def get_account_details(account, company, posting_date, party_type=None, party=N
"balance_in_account_currency": balance_in_account_currency,
"current_exchange_rate": current_exchange_rate,
"new_exchange_rate": new_exchange_rate,
"new_balance_in_base_currency": new_balance_in_base_currency
"new_balance_in_base_currency": new_balance_in_base_currency,
}
return account_details

View File

@@ -1,11 +1,2 @@
def get_data():
return {
'fieldname': 'reference_name',
'transactions': [
{
'items': ['Journal Entry']
}
]
}
return {"fieldname": "reference_name", "transactions": [{"items": ["Journal Entry"]}]}

View File

@@ -1,24 +1,13 @@
from frappe import _
def get_data():
return {
'fieldname': 'finance_book',
'non_standard_fieldnames': {
'Asset': 'default_finance_book',
'Company': 'default_finance_book'
},
'transactions': [
{
'label': _('Assets'),
'items': ['Asset', 'Asset Value Adjustment']
},
{
'items': ['Company']
},
{
'items': ['Journal Entry']
}
]
"fieldname": "finance_book",
"non_standard_fieldnames": {"Asset": "default_finance_book", "Company": "default_finance_book"},
"transactions": [
{"label": _("Assets"), "items": ["Asset", "Asset Value Adjustment"]},
{"items": ["Company"]},
{"items": ["Journal Entry"]},
],
}

View File

@@ -13,31 +13,30 @@ class TestFinanceBook(unittest.TestCase):
finance_book = create_finance_book()
# create jv entry
jv = make_journal_entry("_Test Bank - _TC",
"_Test Receivable - _TC", 100, save=False)
jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable - _TC", 100, save=False)
jv.accounts[1].update({
"party_type": "Customer",
"party": "_Test Customer"
})
jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer"})
jv.finance_book = finance_book.finance_book_name
jv.submit()
# check the Finance Book in the GL Entry
gl_entries = frappe.get_all("GL Entry", fields=["name", "finance_book"],
filters={"voucher_type": "Journal Entry", "voucher_no": jv.name})
gl_entries = frappe.get_all(
"GL Entry",
fields=["name", "finance_book"],
filters={"voucher_type": "Journal Entry", "voucher_no": jv.name},
)
for gl_entry in gl_entries:
self.assertEqual(gl_entry.finance_book, finance_book.name)
def create_finance_book():
if not frappe.db.exists("Finance Book", "_Test Finance Book"):
finance_book = frappe.get_doc({
"doctype": "Finance Book",
"finance_book_name": "_Test Finance Book"
}).insert()
finance_book = frappe.get_doc(
{"doctype": "Finance Book", "finance_book_name": "_Test Finance Book"}
).insert()
else:
finance_book = frappe.get_doc("Finance Book", "_Test Finance Book")
return finance_book
return finance_book

View File

@@ -9,7 +9,9 @@ from frappe.model.document import Document
from frappe.utils import add_days, add_years, cstr, getdate
class FiscalYearIncorrectDate(frappe.ValidationError): pass
class FiscalYearIncorrectDate(frappe.ValidationError):
pass
class FiscalYear(Document):
@frappe.whitelist()
@@ -22,19 +24,33 @@ class FiscalYear(Document):
# 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))
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()
if not self.is_new():
year_start_end_dates = frappe.db.sql("""select year_start_date, year_end_date
from `tabFiscal Year` where name=%s""", (self.name))
year_start_end_dates = frappe.db.sql(
"""select year_start_date, year_end_date
from `tabFiscal Year` where name=%s""",
(self.name),
)
if year_start_end_dates:
if getdate(self.year_start_date) != year_start_end_dates[0][0] or getdate(self.year_end_date) != year_start_end_dates[0][1]:
frappe.throw(_("Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved."))
if (
getdate(self.year_start_date) != year_start_end_dates[0][0]
or getdate(self.year_end_date) != year_start_end_dates[0][1]
):
frappe.throw(
_(
"Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved."
)
)
def validate_dates(self):
if self.is_short_year:
@@ -43,14 +59,18 @@ class FiscalYear(Document):
return
if getdate(self.year_start_date) > getdate(self.year_end_date):
frappe.throw(_("Fiscal Year Start Date should be one year earlier than Fiscal Year End Date"),
FiscalYearIncorrectDate)
frappe.throw(
_("Fiscal Year Start Date should be one year earlier than Fiscal Year End Date"),
FiscalYearIncorrectDate,
)
date = getdate(self.year_start_date) + relativedelta(years=1) - relativedelta(days=1)
if getdate(self.year_end_date) != date:
frappe.throw(_("Fiscal Year End Date should be one year after Fiscal Year Start Date"),
FiscalYearIncorrectDate)
frappe.throw(
_("Fiscal Year End Date should be one year after Fiscal Year Start Date"),
FiscalYearIncorrectDate,
)
def on_update(self):
check_duplicate_fiscal_year(self)
@@ -59,11 +79,16 @@ class FiscalYear(Document):
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.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):
existing_fiscal_years = frappe.db.sql("""select name from `tabFiscal Year`
existing_fiscal_years = frappe.db.sql(
"""select name from `tabFiscal Year`
where (
(%(year_start_date)s between year_start_date and year_end_date)
or (%(year_end_date)s between year_start_date and year_end_date)
@@ -73,13 +98,18 @@ class FiscalYear(Document):
{
"year_start_date": self.year_start_date,
"year_end_date": self.year_end_date,
"name": self.name or "No Name"
}, as_dict=True)
"name": self.name or "No Name",
},
as_dict=True,
)
if existing_fiscal_years:
for existing in existing_fiscal_years:
company_for_existing = frappe.db.sql_list("""select company from `tabFiscal Year Company`
where parent=%s""", existing.name)
company_for_existing = frappe.db.sql_list(
"""select company from `tabFiscal Year Company`
where parent=%s""",
existing.name,
)
overlap = False
if not self.get("companies") or not company_for_existing:
@@ -90,20 +120,36 @@ class FiscalYear(Document):
overlap = True
if overlap:
frappe.throw(_("Year start date or end date is overlapping with {0}. To avoid please set company")
.format(existing.name), frappe.NameError)
frappe.throw(
_("Year start date or end date is overlapping with {0}. To avoid please set company").format(
existing.name
),
frappe.NameError,
)
@frappe.whitelist()
def check_duplicate_fiscal_year(doc):
year_start_end_dates = frappe.db.sql("""select name, year_start_date, year_end_date from `tabFiscal Year` where name!=%s""", (doc.name))
year_start_end_dates = frappe.db.sql(
"""select name, year_start_date, year_end_date from `tabFiscal Year` where name!=%s""",
(doc.name),
)
for fiscal_year, ysd, yed in year_start_end_dates:
if (getdate(doc.year_start_date) == ysd and getdate(doc.year_end_date) == yed) and (not frappe.flags.in_test):
frappe.throw(_("Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}").format(fiscal_year))
if (getdate(doc.year_start_date) == ysd and getdate(doc.year_end_date) == yed) and (
not frappe.flags.in_test
):
frappe.throw(
_("Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}").format(
fiscal_year
)
)
@frappe.whitelist()
def auto_create_fiscal_year():
for d in frappe.db.sql("""select name from `tabFiscal Year` where year_end_date = date_add(current_date, interval 3 day)"""):
for d in frappe.db.sql(
"""select name from `tabFiscal Year` where year_end_date = date_add(current_date, interval 3 day)"""
):
try:
current_fy = frappe.get_doc("Fiscal Year", d[0])
@@ -114,16 +160,14 @@ def auto_create_fiscal_year():
start_year = cstr(new_fy.year_start_date.year)
end_year = cstr(new_fy.year_end_date.year)
new_fy.year = start_year if start_year==end_year else (start_year + "-" + end_year)
new_fy.year = start_year if start_year == end_year else (start_year + "-" + end_year)
new_fy.auto_created = 1
new_fy.insert(ignore_permissions=True)
except frappe.NameError:
pass
def get_from_and_to_date(fiscal_year):
fields = [
"year_start_date as from_date",
"year_end_date as to_date"
]
fields = ["year_start_date as from_date", "year_end_date as to_date"]
return frappe.db.get_value("Fiscal Year", fiscal_year, fields, as_dict=1)

View File

@@ -1,22 +1,15 @@
from frappe import _
def get_data():
return {
'fieldname': 'fiscal_year',
'transactions': [
"fieldname": "fiscal_year",
"transactions": [
{"label": _("Budgets"), "items": ["Budget"]},
{"label": _("References"), "items": ["Period Closing Voucher"]},
{
'label': _('Budgets'),
'items': ['Budget']
"label": _("Target Details"),
"items": ["Sales Person", "Sales Partner", "Territory", "Monthly Distribution"],
},
{
'label': _('References'),
'items': ['Period Closing Voucher']
},
{
'label': _('Target Details'),
'items': ['Sales Person', 'Sales Partner', 'Territory', 'Monthly Distribution']
}
]
],
}

View File

@@ -11,43 +11,48 @@ from erpnext.accounts.doctype.fiscal_year.fiscal_year import FiscalYearIncorrect
test_ignore = ["Company"]
class TestFiscalYear(unittest.TestCase):
class TestFiscalYear(unittest.TestCase):
def test_extra_year(self):
if frappe.db.exists("Fiscal Year", "_Test Fiscal Year 2000"):
frappe.delete_doc("Fiscal Year", "_Test Fiscal Year 2000")
fy = frappe.get_doc({
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2000",
"year_end_date": "2002-12-31",
"year_start_date": "2000-04-01"
})
fy = frappe.get_doc(
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2000",
"year_end_date": "2002-12-31",
"year_start_date": "2000-04-01",
}
)
self.assertRaises(FiscalYearIncorrectDate, fy.insert)
def test_record_generator():
test_records = [
{
"doctype": "Fiscal Year",
"year": "_Test Short Fiscal Year 2011",
"is_short_year": 1,
"year_end_date": "2011-04-01",
"year_start_date": "2011-12-31"
}
{
"doctype": "Fiscal Year",
"year": "_Test Short Fiscal Year 2011",
"is_short_year": 1,
"year_end_date": "2011-04-01",
"year_start_date": "2011-12-31",
}
]
start = 2012
end = now_datetime().year + 5
for year in range(start, end):
test_records.append({
"doctype": "Fiscal Year",
"year": f"_Test Fiscal Year {year}",
"year_start_date": f"{year}-01-01",
"year_end_date": f"{year}-12-31"
})
test_records.append(
{
"doctype": "Fiscal Year",
"year": f"_Test Fiscal Year {year}",
"year_start_date": f"{year}-01-01",
"year_end_date": f"{year}-12-31",
}
)
return test_records
test_records = test_record_generator()

View File

@@ -26,6 +26,8 @@ from erpnext.exceptions import (
)
exclude_from_linked_with = True
class GLEntry(Document):
def autoname(self):
"""
@@ -33,6 +35,8 @@ class GLEntry(Document):
name will be changed using autoname options (in a scheduled job)
"""
self.name = frappe.generate_hash(txt="", length=10)
if self.meta.autoname == "hash":
self.to_rename = 0
def validate(self):
self.flags.ignore_submit_comment = True
@@ -56,14 +60,18 @@ class GLEntry(Document):
validate_frozen_account(self.account, adv_adj)
# Update outstanding amt on against voucher
if (self.against_voucher_type in ['Journal Entry', 'Sales Invoice', 'Purchase Invoice', 'Fees']
and self.against_voucher and self.flags.update_outstanding == 'Yes'
and not frappe.flags.is_reverse_depr_entry):
update_outstanding_amt(self.account, self.party_type, self.party, self.against_voucher_type,
self.against_voucher)
if (
self.against_voucher_type in ["Journal Entry", "Sales Invoice", "Purchase Invoice", "Fees"]
and self.against_voucher
and self.flags.update_outstanding == "Yes"
and not frappe.flags.is_reverse_depr_entry
):
update_outstanding_amt(
self.account, self.party_type, self.party, self.against_voucher_type, self.against_voucher
)
def check_mandatory(self):
mandatory = ['account','voucher_type','voucher_no','company']
mandatory = ["account", "voucher_type", "voucher_no", "company"]
for k in mandatory:
if not self.get(k):
frappe.throw(_("{0} is required").format(_(self.meta.get_label(k))))
@@ -71,29 +79,40 @@ class GLEntry(Document):
if not (self.party_type and self.party):
account_type = frappe.get_cached_value("Account", self.account, "account_type")
if account_type == "Receivable":
frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}")
.format(self.voucher_type, self.voucher_no, self.account))
frappe.throw(
_("{0} {1}: Customer is required against Receivable account {2}").format(
self.voucher_type, self.voucher_no, self.account
)
)
elif account_type == "Payable":
frappe.throw(_("{0} {1}: Supplier is required against Payable account {2}")
.format(self.voucher_type, self.voucher_no, self.account))
frappe.throw(
_("{0} {1}: Supplier is required against Payable account {2}").format(
self.voucher_type, self.voucher_no, self.account
)
)
# Zero value transaction is not allowed
if not (flt(self.debit, self.precision("debit")) or flt(self.credit, self.precision("credit"))):
frappe.throw(_("{0} {1}: Either debit or credit amount is required for {2}")
.format(self.voucher_type, self.voucher_no, self.account))
frappe.throw(
_("{0} {1}: Either debit or credit amount is required for {2}").format(
self.voucher_type, self.voucher_no, self.account
)
)
def pl_must_have_cost_center(self):
"""Validate that profit and loss type account GL entries have a cost center."""
if self.cost_center or self.voucher_type == 'Period Closing Voucher':
if self.cost_center or self.voucher_type == "Period Closing Voucher":
return
if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss":
msg = _("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}.").format(
self.voucher_type, self.voucher_no, self.account)
self.voucher_type, self.voucher_no, self.account
)
msg += " "
msg += _("Please set the cost center field in {0} or setup a default Cost Center for the Company.").format(
self.voucher_type)
msg += _(
"Please set the cost center field in {0} or setup a default Cost Center for the Company."
).format(self.voucher_type)
frappe.throw(msg, title=_("Missing Cost Center"))
@@ -101,17 +120,31 @@ class GLEntry(Document):
account_type = frappe.db.get_value("Account", self.account, "report_type")
for dimension in get_checks_for_pl_and_bs_accounts():
if account_type == "Profit and Loss" \
and self.company == dimension.company and dimension.mandatory_for_pl and not dimension.disabled:
if (
account_type == "Profit and Loss"
and self.company == dimension.company
and dimension.mandatory_for_pl
and not dimension.disabled
):
if not self.get(dimension.fieldname):
frappe.throw(_("Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.")
.format(dimension.label, self.account))
frappe.throw(
_("Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.").format(
dimension.label, self.account
)
)
if account_type == "Balance Sheet" \
and self.company == dimension.company and dimension.mandatory_for_bs and not dimension.disabled:
if (
account_type == "Balance Sheet"
and self.company == dimension.company
and dimension.mandatory_for_bs
and not dimension.disabled
):
if not self.get(dimension.fieldname):
frappe.throw(_("Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.")
.format(dimension.label, self.account))
frappe.throw(
_("Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.").format(
dimension.label, self.account
)
)
def validate_allowed_dimensions(self):
dimension_filter_map = get_dimension_filter_map()
@@ -120,56 +153,97 @@ class GLEntry(Document):
account = key[1]
if self.account == account:
if value['is_mandatory'] and not self.get(dimension):
frappe.throw(_("{0} is mandatory for account {1}").format(
frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), MandatoryAccountDimensionError)
if value["is_mandatory"] and not self.get(dimension):
frappe.throw(
_("{0} is mandatory for account {1}").format(
frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)
),
MandatoryAccountDimensionError,
)
if value['allow_or_restrict'] == 'Allow':
if self.get(dimension) and self.get(dimension) not in value['allowed_dimensions']:
frappe.throw(_("Invalid value {0} for {1} against account {2}").format(
frappe.bold(self.get(dimension)), frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), InvalidAccountDimensionError)
if value["allow_or_restrict"] == "Allow":
if self.get(dimension) and self.get(dimension) not in value["allowed_dimensions"]:
frappe.throw(
_("Invalid value {0} for {1} against account {2}").format(
frappe.bold(self.get(dimension)),
frappe.bold(frappe.unscrub(dimension)),
frappe.bold(self.account),
),
InvalidAccountDimensionError,
)
else:
if self.get(dimension) and self.get(dimension) in value['allowed_dimensions']:
frappe.throw(_("Invalid value {0} for {1} against account {2}").format(
frappe.bold(self.get(dimension)), frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account)), InvalidAccountDimensionError)
if self.get(dimension) and self.get(dimension) in value["allowed_dimensions"]:
frappe.throw(
_("Invalid value {0} for {1} against account {2}").format(
frappe.bold(self.get(dimension)),
frappe.bold(frappe.unscrub(dimension)),
frappe.bold(self.account),
),
InvalidAccountDimensionError,
)
def check_pl_account(self):
if self.is_opening=='Yes' and \
frappe.db.get_value("Account", self.account, "report_type")=="Profit and Loss":
frappe.throw(_("{0} {1}: 'Profit and Loss' type account {2} not allowed in Opening Entry")
.format(self.voucher_type, self.voucher_no, self.account))
if (
self.is_opening == "Yes"
and frappe.db.get_value("Account", self.account, "report_type") == "Profit and Loss"
and not self.is_cancelled
):
frappe.throw(
_("{0} {1}: 'Profit and Loss' type account {2} not allowed in Opening Entry").format(
self.voucher_type, self.voucher_no, self.account
)
)
def validate_account_details(self, adv_adj):
"""Account must be ledger, active and not freezed"""
ret = frappe.db.sql("""select is_group, docstatus, company
from tabAccount where name=%s""", self.account, as_dict=1)[0]
ret = frappe.db.sql(
"""select is_group, docstatus, company
from tabAccount where name=%s""",
self.account,
as_dict=1,
)[0]
if ret.is_group==1:
frappe.throw(_('''{0} {1}: Account {2} is a Group Account and group accounts cannot be used in transactions''')
.format(self.voucher_type, self.voucher_no, self.account))
if ret.is_group == 1:
frappe.throw(
_(
"""{0} {1}: Account {2} is a Group Account and group accounts cannot be used in transactions"""
).format(self.voucher_type, self.voucher_no, self.account)
)
if ret.docstatus==2:
frappe.throw(_("{0} {1}: Account {2} is inactive")
.format(self.voucher_type, self.voucher_no, self.account))
if ret.docstatus == 2:
frappe.throw(
_("{0} {1}: Account {2} is inactive").format(self.voucher_type, self.voucher_no, self.account)
)
if ret.company != self.company:
frappe.throw(_("{0} {1}: Account {2} does not belong to Company {3}")
.format(self.voucher_type, self.voucher_no, self.account, self.company))
frappe.throw(
_("{0} {1}: Account {2} does not belong to Company {3}").format(
self.voucher_type, self.voucher_no, self.account, self.company
)
)
def validate_cost_center(self):
if not self.cost_center: return
if not self.cost_center:
return
is_group, company = frappe.get_cached_value('Cost Center',
self.cost_center, ['is_group', 'company'])
is_group, company = frappe.get_cached_value(
"Cost Center", self.cost_center, ["is_group", "company"]
)
if company != self.company:
frappe.throw(_("{0} {1}: Cost Center {2} does not belong to Company {3}")
.format(self.voucher_type, self.voucher_no, self.cost_center, self.company))
frappe.throw(
_("{0} {1}: Cost Center {2} does not belong to Company {3}").format(
self.voucher_type, self.voucher_no, self.cost_center, self.company
)
)
if (self.voucher_type != 'Period Closing Voucher' and is_group):
frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions""").format(
self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
if self.voucher_type != "Period Closing Voucher" and is_group:
frappe.throw(
_(
"""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions"""
).format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center))
)
def validate_party(self):
validate_party_frozen_disabled(self.party_type, self.party)
@@ -182,9 +256,12 @@ class GLEntry(Document):
self.account_currency = account_currency or company_currency
if account_currency != self.account_currency:
frappe.throw(_("{0} {1}: Accounting Entry for {2} can only be made in currency: {3}")
.format(self.voucher_type, self.voucher_no, self.account,
(account_currency or company_currency)), InvalidAccountCurrency)
frappe.throw(
_("{0} {1}: Accounting Entry for {2} can only be made in currency: {3}").format(
self.voucher_type, self.voucher_no, self.account, (account_currency or company_currency)
),
InvalidAccountCurrency,
)
if self.party_type and self.party:
validate_party_gle_currency(self.party_type, self.party, self.company, self.account_currency)
@@ -193,51 +270,80 @@ class GLEntry(Document):
if not self.fiscal_year:
self.fiscal_year = get_fiscal_year(self.posting_date, company=self.company)[0]
def validate_balance_type(account, adv_adj=False):
if not adv_adj and account:
balance_must_be = frappe.db.get_value("Account", account, "balance_must_be")
if balance_must_be:
balance = frappe.db.sql("""select sum(debit) - sum(credit)
from `tabGL Entry` where account = %s""", account)[0][0]
balance = frappe.db.sql(
"""select sum(debit) - sum(credit)
from `tabGL Entry` where account = %s""",
account,
)[0][0]
if (balance_must_be=="Debit" and flt(balance) < 0) or \
(balance_must_be=="Credit" and flt(balance) > 0):
frappe.throw(_("Balance for Account {0} must always be {1}").format(account, _(balance_must_be)))
if (balance_must_be == "Debit" and flt(balance) < 0) or (
balance_must_be == "Credit" and flt(balance) > 0
):
frappe.throw(
_("Balance for Account {0} must always be {1}").format(account, _(balance_must_be))
)
def update_outstanding_amt(account, party_type, party, against_voucher_type, against_voucher, on_cancel=False):
def update_outstanding_amt(
account, party_type, party, against_voucher_type, against_voucher, on_cancel=False
):
if party_type and party:
party_condition = " and party_type={0} and party={1}"\
.format(frappe.db.escape(party_type), frappe.db.escape(party))
party_condition = " and party_type={0} and party={1}".format(
frappe.db.escape(party_type), frappe.db.escape(party)
)
else:
party_condition = ""
if against_voucher_type == "Sales Invoice":
party_account = frappe.db.get_value(against_voucher_type, against_voucher, "debit_to")
account_condition = "and account in ({0}, {1})".format(frappe.db.escape(account), frappe.db.escape(party_account))
account_condition = "and account in ({0}, {1})".format(
frappe.db.escape(account), frappe.db.escape(party_account)
)
else:
account_condition = " and account = {0}".format(frappe.db.escape(account))
# get final outstanding amt
bal = flt(frappe.db.sql("""
bal = flt(
frappe.db.sql(
"""
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
from `tabGL Entry`
where against_voucher_type=%s and against_voucher=%s
and voucher_type != 'Invoice Discounting'
{0} {1}""".format(party_condition, account_condition),
(against_voucher_type, against_voucher))[0][0] or 0.0)
{0} {1}""".format(
party_condition, account_condition
),
(against_voucher_type, against_voucher),
)[0][0]
or 0.0
)
if against_voucher_type == 'Purchase Invoice':
if against_voucher_type == "Purchase Invoice":
bal = -bal
elif against_voucher_type == "Journal Entry":
against_voucher_amount = flt(frappe.db.sql("""
against_voucher_amount = flt(
frappe.db.sql(
"""
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
from `tabGL Entry` where voucher_type = 'Journal Entry' and voucher_no = %s
and account = %s and (against_voucher is null or against_voucher='') {0}"""
.format(party_condition), (against_voucher, account))[0][0])
and account = %s and (against_voucher is null or against_voucher='') {0}""".format(
party_condition
),
(against_voucher, account),
)[0][0]
)
if not against_voucher_amount:
frappe.throw(_("Against Journal Entry {0} is already adjusted against some other voucher")
.format(against_voucher))
frappe.throw(
_("Against Journal Entry {0} is already adjusted against some other voucher").format(
against_voucher
)
)
bal = against_voucher_amount + bal
if against_voucher_amount < 0:
@@ -245,44 +351,51 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga
# Validation : Outstanding can not be negative for JV
if bal < 0 and not on_cancel:
frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal)))
frappe.throw(
_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal))
)
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
# Didn't use db_set for optimisation purpose
ref_doc.outstanding_amount = bal
frappe.db.set_value(against_voucher_type, against_voucher, 'outstanding_amount', bal)
frappe.db.set_value(against_voucher_type, against_voucher, "outstanding_amount", bal)
ref_doc.set_status(update=True)
def validate_frozen_account(account, adv_adj=None):
frozen_account = frappe.get_cached_value("Account", account, "freeze_account")
if frozen_account == 'Yes' and not adv_adj:
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,
'frozen_accounts_modifier')
if frozen_account == "Yes" and not adv_adj:
frozen_accounts_modifier = frappe.db.get_value(
"Accounts Settings", None, "frozen_accounts_modifier"
)
if not frozen_accounts_modifier:
frappe.throw(_("Account {0} is frozen").format(account))
elif frozen_accounts_modifier not in frappe.get_roles():
frappe.throw(_("Not authorized to edit frozen Account {0}").format(account))
def update_against_account(voucher_type, voucher_no):
entries = frappe.db.get_all("GL Entry",
entries = frappe.db.get_all(
"GL Entry",
filters={"voucher_type": voucher_type, "voucher_no": voucher_no},
fields=["name", "party", "against", "debit", "credit", "account", "company"])
fields=["name", "party", "against", "debit", "credit", "account", "company"],
)
if not entries:
return
company_currency = erpnext.get_company_currency(entries[0].company)
precision = get_field_precision(frappe.get_meta("GL Entry")
.get_field("debit"), company_currency)
precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency)
accounts_debited, accounts_credited = [], []
for d in entries:
if flt(d.debit, precision) > 0: accounts_debited.append(d.party or d.account)
if flt(d.credit, precision) > 0: accounts_credited.append(d.party or d.account)
if flt(d.debit, precision) > 0:
accounts_debited.append(d.party or d.account)
if flt(d.credit, precision) > 0:
accounts_credited.append(d.party or d.account)
for d in entries:
if flt(d.debit, precision) > 0:
@@ -293,14 +406,17 @@ def update_against_account(voucher_type, voucher_no):
if d.against != new_against:
frappe.db.set_value("GL Entry", d.name, "against", new_against)
def on_doctype_update():
frappe.db.add_index("GL Entry", ["against_voucher_type", "against_voucher"])
frappe.db.add_index("GL Entry", ["voucher_type", "voucher_no"])
def rename_gle_sle_docs():
for doctype in ["GL Entry", "Stock Ledger Entry"]:
rename_temporarily_named_docs(doctype)
def rename_temporarily_named_docs(doctype):
"""Rename temporarily named docs using autoname options"""
docs_to_rename = frappe.get_all(doctype, {"to_rename": "1"}, order_by="creation", limit=50000)
@@ -311,5 +427,5 @@ def rename_temporarily_named_docs(doctype):
frappe.db.sql(
"UPDATE `tab{}` SET name = %s, to_rename = 0 where name = %s".format(doctype),
(newname, oldname),
auto_commit=True
auto_commit=True,
)

View File

@@ -14,48 +14,68 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
class TestGLEntry(unittest.TestCase):
def test_round_off_entry(self):
frappe.db.set_value("Company", "_Test Company", "round_off_account", "_Test Write Off - _TC")
frappe.db.set_value("Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC")
frappe.db.set_value(
"Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC"
)
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 100, "_Test Cost Center - _TC", submit=False)
jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC",
100,
"_Test Cost Center - _TC",
submit=False,
)
jv.get("accounts")[0].debit = 100.01
jv.flags.ignore_validate = True
jv.submit()
round_off_entry = frappe.db.sql("""select name from `tabGL Entry`
round_off_entry = frappe.db.sql(
"""select name from `tabGL Entry`
where voucher_type='Journal Entry' and voucher_no = %s
and account='_Test Write Off - _TC' and cost_center='_Test Cost Center - _TC'
and debit = 0 and credit = '.01'""", jv.name)
and debit = 0 and credit = '.01'""",
jv.name,
)
self.assertTrue(round_off_entry)
def test_rename_entries(self):
je = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 100, submit=True)
je = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 100, submit=True
)
rename_gle_sle_docs()
naming_series = parse_naming_series(parts=frappe.get_meta("GL Entry").autoname.split(".")[:-1])
je = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 100, submit=True)
je = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 100, submit=True
)
gl_entries = frappe.get_all("GL Entry",
gl_entries = frappe.get_all(
"GL Entry",
fields=["name", "to_rename"],
filters={"voucher_type": "Journal Entry", "voucher_no": je.name},
order_by="creation"
order_by="creation",
)
self.assertTrue(all(entry.to_rename == 1 for entry in gl_entries))
old_naming_series_current_value = frappe.db.sql("SELECT current from tabSeries where name = %s", naming_series)[0][0]
old_naming_series_current_value = frappe.db.sql(
"SELECT current from tabSeries where name = %s", naming_series
)[0][0]
rename_gle_sle_docs()
new_gl_entries = frappe.get_all("GL Entry",
new_gl_entries = frappe.get_all(
"GL Entry",
fields=["name", "to_rename"],
filters={"voucher_type": "Journal Entry", "voucher_no": je.name},
order_by="creation"
order_by="creation",
)
self.assertTrue(all(entry.to_rename == 0 for entry in new_gl_entries))
self.assertTrue(all(new.name != old.name for new, old in zip(gl_entries, new_gl_entries)))
new_naming_series_current_value = frappe.db.sql("SELECT current from tabSeries where name = %s", naming_series)[0][0]
new_naming_series_current_value = frappe.db.sql(
"SELECT current from tabSeries where name = %s", naming_series
)[0][0]
self.assertEqual(old_naming_series_current_value + 2, new_naming_series_current_value)

View File

@@ -10,6 +10,7 @@
"sgst_account",
"igst_account",
"cess_account",
"utgst_account",
"is_reverse_charge_account"
],
"fields": [
@@ -64,12 +65,18 @@
"fieldtype": "Check",
"in_list_view": 1,
"label": "Is Reverse Charge Account"
},
{
"fieldname": "utgst_account",
"fieldtype": "Link",
"label": "UTGST Account",
"options": "Account"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-04-09 12:30:25.889993",
"modified": "2022-04-07 12:59:14.039768",
"modified_by": "Administrator",
"module": "Accounts",
"name": "GST Account",
@@ -78,5 +85,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -33,19 +33,32 @@ class InvoiceDiscounting(AccountsController):
frappe.throw(_("Loan Start Date and Loan Period are mandatory to save the Invoice Discounting"))
def validate_invoices(self):
discounted_invoices = [record.sales_invoice for record in
frappe.get_all("Discounted Invoice",fields=["sales_invoice"], filters={"docstatus":1})]
discounted_invoices = [
record.sales_invoice
for record in frappe.get_all(
"Discounted Invoice", fields=["sales_invoice"], filters={"docstatus": 1}
)
]
for record in self.invoices:
if record.sales_invoice in discounted_invoices:
frappe.throw(_("Row({0}): {1} is already discounted in {2}")
.format(record.idx, frappe.bold(record.sales_invoice), frappe.bold(record.parent)))
frappe.throw(
_("Row({0}): {1} is already discounted in {2}").format(
record.idx, frappe.bold(record.sales_invoice), frappe.bold(record.parent)
)
)
actual_outstanding = frappe.db.get_value("Sales Invoice", record.sales_invoice,"outstanding_amount")
if record.outstanding_amount > actual_outstanding :
frappe.throw(_
("Row({0}): Outstanding Amount cannot be greater than actual Outstanding Amount {1} in {2}").format(
record.idx, frappe.bold(actual_outstanding), frappe.bold(record.sales_invoice)))
actual_outstanding = frappe.db.get_value(
"Sales Invoice", record.sales_invoice, "outstanding_amount"
)
if record.outstanding_amount > actual_outstanding:
frappe.throw(
_(
"Row({0}): Outstanding Amount cannot be greater than actual Outstanding Amount {1} in {2}"
).format(
record.idx, frappe.bold(actual_outstanding), frappe.bold(record.sales_invoice)
)
)
def calculate_total_amount(self):
self.total_amount = sum(flt(d.outstanding_amount) for d in self.invoices)
@@ -73,24 +86,21 @@ class InvoiceDiscounting(AccountsController):
self.status = "Cancelled"
if cancel:
self.db_set('status', self.status, update_modified = True)
self.db_set("status", self.status, update_modified=True)
def update_sales_invoice(self):
for d in self.invoices:
if self.docstatus == 1:
is_discounted = 1
else:
discounted_invoice = frappe.db.exists({
"doctype": "Discounted Invoice",
"sales_invoice": d.sales_invoice,
"docstatus": 1
})
discounted_invoice = frappe.db.exists(
{"doctype": "Discounted Invoice", "sales_invoice": d.sales_invoice, "docstatus": 1}
)
is_discounted = 1 if discounted_invoice else 0
frappe.db.set_value("Sales Invoice", d.sales_invoice, "is_discounted", is_discounted)
def make_gl_entries(self):
company_currency = frappe.get_cached_value('Company', self.company, "default_currency")
company_currency = frappe.get_cached_value("Company", self.company, "default_currency")
gl_entries = []
invoice_fields = ["debit_to", "party_account_currency", "conversion_rate", "cost_center"]
@@ -102,135 +112,182 @@ class InvoiceDiscounting(AccountsController):
inv = frappe.db.get_value("Sales Invoice", d.sales_invoice, invoice_fields, as_dict=1)
if d.outstanding_amount:
outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate,
d.precision("outstanding_amount"))
ar_credit_account_currency = frappe.get_cached_value("Account", self.accounts_receivable_credit, "currency")
outstanding_in_company_currency = flt(
d.outstanding_amount * inv.conversion_rate, d.precision("outstanding_amount")
)
ar_credit_account_currency = frappe.get_cached_value(
"Account", self.accounts_receivable_credit, "currency"
)
gl_entries.append(self.get_gl_dict({
"account": inv.debit_to,
"party_type": "Customer",
"party": d.customer,
"against": self.accounts_receivable_credit,
"credit": outstanding_in_company_currency,
"credit_in_account_currency": outstanding_in_company_currency \
if inv.party_account_currency==company_currency else d.outstanding_amount,
"cost_center": inv.cost_center,
"against_voucher": d.sales_invoice,
"against_voucher_type": "Sales Invoice"
}, inv.party_account_currency, item=inv))
gl_entries.append(
self.get_gl_dict(
{
"account": inv.debit_to,
"party_type": "Customer",
"party": d.customer,
"against": self.accounts_receivable_credit,
"credit": outstanding_in_company_currency,
"credit_in_account_currency": outstanding_in_company_currency
if inv.party_account_currency == company_currency
else d.outstanding_amount,
"cost_center": inv.cost_center,
"against_voucher": d.sales_invoice,
"against_voucher_type": "Sales Invoice",
},
inv.party_account_currency,
item=inv,
)
)
gl_entries.append(self.get_gl_dict({
"account": self.accounts_receivable_credit,
"party_type": "Customer",
"party": d.customer,
"against": inv.debit_to,
"debit": outstanding_in_company_currency,
"debit_in_account_currency": outstanding_in_company_currency \
if ar_credit_account_currency==company_currency else d.outstanding_amount,
"cost_center": inv.cost_center,
"against_voucher": d.sales_invoice,
"against_voucher_type": "Sales Invoice"
}, ar_credit_account_currency, item=inv))
gl_entries.append(
self.get_gl_dict(
{
"account": self.accounts_receivable_credit,
"party_type": "Customer",
"party": d.customer,
"against": inv.debit_to,
"debit": outstanding_in_company_currency,
"debit_in_account_currency": outstanding_in_company_currency
if ar_credit_account_currency == company_currency
else d.outstanding_amount,
"cost_center": inv.cost_center,
"against_voucher": d.sales_invoice,
"against_voucher_type": "Sales Invoice",
},
ar_credit_account_currency,
item=inv,
)
)
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding="No")
@frappe.whitelist()
def create_disbursement_entry(self):
je = frappe.new_doc("Journal Entry")
je.voucher_type = 'Journal Entry'
je.voucher_type = "Journal Entry"
je.company = self.company
je.remark = 'Loan Disbursement entry against Invoice Discounting: ' + self.name
je.remark = "Loan Disbursement entry against Invoice Discounting: " + self.name
je.append("accounts", {
"account": self.bank_account,
"debit_in_account_currency": flt(self.total_amount) - flt(self.bank_charges),
"cost_center": erpnext.get_default_cost_center(self.company)
})
je.append(
"accounts",
{
"account": self.bank_account,
"debit_in_account_currency": flt(self.total_amount) - flt(self.bank_charges),
"cost_center": erpnext.get_default_cost_center(self.company),
},
)
if self.bank_charges:
je.append("accounts", {
"account": self.bank_charges_account,
"debit_in_account_currency": flt(self.bank_charges),
"cost_center": erpnext.get_default_cost_center(self.company)
})
je.append(
"accounts",
{
"account": self.bank_charges_account,
"debit_in_account_currency": flt(self.bank_charges),
"cost_center": erpnext.get_default_cost_center(self.company),
},
)
je.append("accounts", {
"account": self.short_term_loan,
"credit_in_account_currency": flt(self.total_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting",
"reference_name": self.name
})
je.append(
"accounts",
{
"account": self.short_term_loan,
"credit_in_account_currency": flt(self.total_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
},
)
for d in self.invoices:
je.append("accounts", {
"account": self.accounts_receivable_discounted,
"debit_in_account_currency": flt(d.outstanding_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer
})
je.append(
"accounts",
{
"account": self.accounts_receivable_discounted,
"debit_in_account_currency": flt(d.outstanding_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer,
},
)
je.append("accounts", {
"account": self.accounts_receivable_credit,
"credit_in_account_currency": flt(d.outstanding_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer
})
je.append(
"accounts",
{
"account": self.accounts_receivable_credit,
"credit_in_account_currency": flt(d.outstanding_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer,
},
)
return je
@frappe.whitelist()
def close_loan(self):
je = frappe.new_doc("Journal Entry")
je.voucher_type = 'Journal Entry'
je.voucher_type = "Journal Entry"
je.company = self.company
je.remark = 'Loan Settlement entry against Invoice Discounting: ' + self.name
je.remark = "Loan Settlement entry against Invoice Discounting: " + self.name
je.append("accounts", {
"account": self.short_term_loan,
"debit_in_account_currency": flt(self.total_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
})
je.append(
"accounts",
{
"account": self.short_term_loan,
"debit_in_account_currency": flt(self.total_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
},
)
je.append("accounts", {
"account": self.bank_account,
"credit_in_account_currency": flt(self.total_amount),
"cost_center": erpnext.get_default_cost_center(self.company)
})
je.append(
"accounts",
{
"account": self.bank_account,
"credit_in_account_currency": flt(self.total_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
},
)
if getdate(self.loan_end_date) > getdate(nowdate()):
for d in self.invoices:
outstanding_amount = frappe.db.get_value("Sales Invoice", d.sales_invoice, "outstanding_amount")
outstanding_amount = frappe.db.get_value(
"Sales Invoice", d.sales_invoice, "outstanding_amount"
)
if flt(outstanding_amount) > 0:
je.append("accounts", {
"account": self.accounts_receivable_discounted,
"credit_in_account_currency": flt(outstanding_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer
})
je.append(
"accounts",
{
"account": self.accounts_receivable_discounted,
"credit_in_account_currency": flt(outstanding_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer,
},
)
je.append("accounts", {
"account": self.accounts_receivable_unpaid,
"debit_in_account_currency": flt(outstanding_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer
})
je.append(
"accounts",
{
"account": self.accounts_receivable_unpaid,
"debit_in_account_currency": flt(outstanding_amount),
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Invoice Discounting",
"reference_name": self.name,
"party_type": "Customer",
"party": d.customer,
},
)
return je
@frappe.whitelist()
def get_invoices(filters):
filters = frappe._dict(json.loads(filters))
@@ -250,7 +307,8 @@ def get_invoices(filters):
if cond:
where_condition += " and " + " and ".join(cond)
return frappe.db.sql("""
return frappe.db.sql(
"""
select
name as sales_invoice,
customer,
@@ -264,17 +322,26 @@ def get_invoices(filters):
%s
and not exists(select di.name from `tabDiscounted Invoice` di
where di.docstatus=1 and di.sales_invoice=si.name)
""" % where_condition, filters, as_dict=1)
"""
% where_condition,
filters,
as_dict=1,
)
def get_party_account_based_on_invoice_discounting(sales_invoice):
party_account = None
invoice_discounting = frappe.db.sql("""
invoice_discounting = frappe.db.sql(
"""
select par.accounts_receivable_discounted, par.accounts_receivable_unpaid, par.status
from `tabInvoice Discounting` par, `tabDiscounted Invoice` ch
where par.name=ch.parent
and par.docstatus=1
and ch.sales_invoice = %s
""", (sales_invoice), as_dict=1)
""",
(sales_invoice),
as_dict=1,
)
if invoice_discounting:
if invoice_discounting[0].status == "Disbursed":
party_account = invoice_discounting[0].accounts_receivable_discounted

View File

@@ -1,21 +1,12 @@
from frappe import _
def get_data():
return {
'fieldname': 'reference_name',
'internal_links': {
'Sales Invoice': ['invoices', 'sales_invoice']
},
'transactions': [
{
'label': _('Reference'),
'items': ['Sales Invoice']
},
{
'label': _('Payment'),
'items': ['Payment Entry', 'Journal Entry']
}
]
"fieldname": "reference_name",
"internal_links": {"Sales Invoice": ["invoices", "sales_invoice"]},
"transactions": [
{"label": _("Reference"), "items": ["Sales Invoice"]},
{"label": _("Payment"), "items": ["Payment Entry", "Journal Entry"]},
],
}

View File

@@ -14,52 +14,74 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_
class TestInvoiceDiscounting(unittest.TestCase):
def setUp(self):
self.ar_credit = create_account(account_name="_Test Accounts Receivable Credit", parent_account = "Accounts Receivable - _TC", company="_Test Company")
self.ar_discounted = create_account(account_name="_Test Accounts Receivable Discounted", parent_account = "Accounts Receivable - _TC", company="_Test Company")
self.ar_unpaid = create_account(account_name="_Test Accounts Receivable Unpaid", parent_account = "Accounts Receivable - _TC", company="_Test Company")
self.short_term_loan = create_account(account_name="_Test Short Term Loan", parent_account = "Source of Funds (Liabilities) - _TC", company="_Test Company")
self.bank_account = create_account(account_name="_Test Bank 2", parent_account = "Bank Accounts - _TC", company="_Test Company")
self.bank_charges_account = create_account(account_name="_Test Bank Charges Account", parent_account = "Expenses - _TC", company="_Test Company")
self.ar_credit = create_account(
account_name="_Test Accounts Receivable Credit",
parent_account="Accounts Receivable - _TC",
company="_Test Company",
)
self.ar_discounted = create_account(
account_name="_Test Accounts Receivable Discounted",
parent_account="Accounts Receivable - _TC",
company="_Test Company",
)
self.ar_unpaid = create_account(
account_name="_Test Accounts Receivable Unpaid",
parent_account="Accounts Receivable - _TC",
company="_Test Company",
)
self.short_term_loan = create_account(
account_name="_Test Short Term Loan",
parent_account="Source of Funds (Liabilities) - _TC",
company="_Test Company",
)
self.bank_account = create_account(
account_name="_Test Bank 2", parent_account="Bank Accounts - _TC", company="_Test Company"
)
self.bank_charges_account = create_account(
account_name="_Test Bank Charges Account",
parent_account="Expenses - _TC",
company="_Test Company",
)
frappe.db.set_value("Company", "_Test Company", "default_bank_account", self.bank_account)
def test_total_amount(self):
inv1 = create_sales_invoice(rate=200)
inv2 = create_sales_invoice(rate=500)
inv_disc = create_invoice_discounting([inv1.name, inv2.name],
inv_disc = create_invoice_discounting(
[inv1.name, inv2.name],
do_not_submit=True,
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account
)
bank_account=self.bank_account,
)
self.assertEqual(inv_disc.total_amount, 700)
def test_gl_entries_in_base_currency(self):
inv = create_sales_invoice(rate=200)
inv_disc = create_invoice_discounting([inv.name],
inv_disc = create_invoice_discounting(
[inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account
)
bank_account=self.bank_account,
)
gle = get_gl_entries("Invoice Discounting", inv_disc.name)
expected_gle = {
inv.debit_to: [0.0, 200],
self.ar_credit: [200, 0.0]
}
expected_gle = {inv.debit_to: [0.0, 200], self.ar_credit: [200, 0.0]}
for i, gle in enumerate(gle):
self.assertEqual([gle.debit, gle.credit], expected_gle.get(gle.account))
def test_loan_on_submit(self):
inv = create_sales_invoice(rate=300)
inv_disc = create_invoice_discounting([inv.name],
inv_disc = create_invoice_discounting(
[inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
@@ -67,28 +89,33 @@ class TestInvoiceDiscounting(unittest.TestCase):
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=nowdate(),
period=60
)
period=60,
)
self.assertEqual(inv_disc.status, "Sanctioned")
self.assertEqual(inv_disc.loan_end_date, add_days(inv_disc.loan_start_date, inv_disc.loan_period))
self.assertEqual(
inv_disc.loan_end_date, add_days(inv_disc.loan_start_date, inv_disc.loan_period)
)
def test_on_disbursed(self):
inv = create_sales_invoice(rate=500)
inv_disc = create_invoice_discounting([inv.name],
inv_disc = create_invoice_discounting(
[inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
bank_charges=100
)
bank_charges=100,
)
je = inv_disc.create_disbursement_entry()
self.assertEqual(je.accounts[0].account, self.bank_account)
self.assertEqual(je.accounts[0].debit_in_account_currency, flt(inv_disc.total_amount) - flt(inv_disc.bank_charges))
self.assertEqual(
je.accounts[0].debit_in_account_currency,
flt(inv_disc.total_amount) - flt(inv_disc.bank_charges),
)
self.assertEqual(je.accounts[1].account, self.bank_charges_account)
self.assertEqual(je.accounts[1].debit_in_account_currency, flt(inv_disc.bank_charges))
@@ -102,7 +129,6 @@ class TestInvoiceDiscounting(unittest.TestCase):
self.assertEqual(je.accounts[4].account, self.ar_credit)
self.assertEqual(je.accounts[4].credit_in_account_currency, flt(inv.outstanding_amount))
je.posting_date = nowdate()
je.submit()
@@ -114,7 +140,8 @@ class TestInvoiceDiscounting(unittest.TestCase):
def test_on_close_after_loan_period(self):
inv = create_sales_invoice(rate=600)
inv_disc = create_invoice_discounting([inv.name],
inv_disc = create_invoice_discounting(
[inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
@@ -122,8 +149,8 @@ class TestInvoiceDiscounting(unittest.TestCase):
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=nowdate(),
period=60
)
period=60,
)
je1 = inv_disc.create_disbursement_entry()
je1.posting_date = nowdate()
@@ -151,7 +178,8 @@ class TestInvoiceDiscounting(unittest.TestCase):
def test_on_close_after_loan_period_after_inv_payment(self):
inv = create_sales_invoice(rate=600)
inv_disc = create_invoice_discounting([inv.name],
inv_disc = create_invoice_discounting(
[inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
@@ -159,8 +187,8 @@ class TestInvoiceDiscounting(unittest.TestCase):
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=nowdate(),
period=60
)
period=60,
)
je1 = inv_disc.create_disbursement_entry()
je1.posting_date = nowdate()
@@ -183,7 +211,8 @@ class TestInvoiceDiscounting(unittest.TestCase):
def test_on_close_before_loan_period(self):
inv = create_sales_invoice(rate=700)
inv_disc = create_invoice_discounting([inv.name],
inv_disc = create_invoice_discounting(
[inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
@@ -191,7 +220,7 @@ class TestInvoiceDiscounting(unittest.TestCase):
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
start=add_days(nowdate(), -80),
period=60
period=60,
)
je1 = inv_disc.create_disbursement_entry()
@@ -209,16 +238,17 @@ class TestInvoiceDiscounting(unittest.TestCase):
self.assertEqual(je2.accounts[1].credit_in_account_currency, flt(inv_disc.total_amount))
def test_make_payment_before_loan_period(self):
#it has problem
# it has problem
inv = create_sales_invoice(rate=700)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account
)
inv_disc = create_invoice_discounting(
[inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
)
je = inv_disc.create_disbursement_entry()
inv_disc.reload()
je.posting_date = nowdate()
@@ -232,26 +262,31 @@ class TestInvoiceDiscounting(unittest.TestCase):
je_on_payment.submit()
self.assertEqual(je_on_payment.accounts[0].account, self.ar_discounted)
self.assertEqual(je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount))
self.assertEqual(
je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount)
)
self.assertEqual(je_on_payment.accounts[1].account, self.bank_account)
self.assertEqual(je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount))
self.assertEqual(
je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount)
)
inv.reload()
self.assertEqual(inv.outstanding_amount, 0)
def test_make_payment_before_after_period(self):
#it has problem
# it has problem
inv = create_sales_invoice(rate=700)
inv_disc = create_invoice_discounting([inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
loan_start_date=add_days(nowdate(), -10),
period=5
)
inv_disc = create_invoice_discounting(
[inv.name],
accounts_receivable_credit=self.ar_credit,
accounts_receivable_discounted=self.ar_discounted,
accounts_receivable_unpaid=self.ar_unpaid,
short_term_loan=self.short_term_loan,
bank_charges_account=self.bank_charges_account,
bank_account=self.bank_account,
loan_start_date=add_days(nowdate(), -10),
period=5,
)
je = inv_disc.create_disbursement_entry()
inv_disc.reload()
je.posting_date = nowdate()
@@ -269,9 +304,13 @@ class TestInvoiceDiscounting(unittest.TestCase):
je_on_payment.submit()
self.assertEqual(je_on_payment.accounts[0].account, self.ar_unpaid)
self.assertEqual(je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount))
self.assertEqual(
je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount)
)
self.assertEqual(je_on_payment.accounts[1].account, self.bank_account)
self.assertEqual(je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount))
self.assertEqual(
je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount)
)
inv.reload()
self.assertEqual(inv.outstanding_amount, 0)
@@ -287,17 +326,15 @@ def create_invoice_discounting(invoices, **args):
inv_disc.accounts_receivable_credit = args.accounts_receivable_credit
inv_disc.accounts_receivable_discounted = args.accounts_receivable_discounted
inv_disc.accounts_receivable_unpaid = args.accounts_receivable_unpaid
inv_disc.short_term_loan=args.short_term_loan
inv_disc.bank_charges_account=args.bank_charges_account
inv_disc.bank_account=args.bank_account
inv_disc.short_term_loan = args.short_term_loan
inv_disc.bank_charges_account = args.bank_charges_account
inv_disc.bank_account = args.bank_account
inv_disc.loan_start_date = args.start or nowdate()
inv_disc.loan_period = args.period or 30
inv_disc.bank_charges = flt(args.bank_charges)
for d in invoices:
inv_disc.append("invoices", {
"sales_invoice": d
})
inv_disc.append("invoices", {"sales_invoice": d})
inv_disc.insert()
if not args.do_not_submit:

View File

@@ -13,20 +13,28 @@ class ItemTaxTemplate(Document):
def autoname(self):
if self.company and self.title:
abbr = frappe.get_cached_value('Company', self.company, 'abbr')
self.name = '{0} - {1}'.format(self.title, abbr)
abbr = frappe.get_cached_value("Company", self.company, "abbr")
self.name = "{0} - {1}".format(self.title, abbr)
def validate_tax_accounts(self):
"""Check whether Tax Rate is not entered twice for same Tax Type"""
check_list = []
for d in self.get('taxes'):
for d in self.get("taxes"):
if d.tax_type:
account_type = frappe.db.get_value("Account", d.tax_type, "account_type")
if account_type not in ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']:
if account_type not in [
"Tax",
"Chargeable",
"Income Account",
"Expense Account",
"Expenses Included In Valuation",
]:
frappe.throw(
_("Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable").format(
d.idx))
_(
"Item Tax Row {0} must have account of type Tax or Income or Expense or Chargeable"
).format(d.idx)
)
else:
if d.tax_type in check_list:
frappe.throw(_("{0} entered twice in Item Tax").format(d.tax_type))

View File

@@ -1,26 +1,13 @@
from frappe import _
def get_data():
return {
'fieldname': 'item_tax_template',
'transactions': [
{
'label': _('Pre Sales'),
'items': ['Quotation', 'Supplier Quotation']
},
{
'label': _('Sales'),
'items': ['Sales Invoice', 'Sales Order', 'Delivery Note']
},
{
'label': _('Purchase'),
'items': ['Purchase Invoice', 'Purchase Order', 'Purchase Receipt']
},
{
'label': _('Stock'),
'items': ['Item Groups', 'Item']
}
]
"fieldname": "item_tax_template",
"transactions": [
{"label": _("Pre Sales"), "items": ["Quotation", "Supplier Quotation"]},
{"label": _("Sales"), "items": ["Sales Invoice", "Sales Order", "Delivery Note"]},
{"label": _("Purchase"), "items": ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]},
{"label": _("Stock"), "items": ["Item Groups", "Item"]},
],
}

View File

@@ -3,7 +3,7 @@
"allow_auto_repeat": 1,
"allow_import": 1,
"autoname": "naming_series:",
"creation": "2013-03-25 10:53:52",
"creation": "2022-01-25 10:29:58.717206",
"doctype": "DocType",
"document_type": "Document",
"engine": "InnoDB",
@@ -13,6 +13,7 @@
"voucher_type",
"naming_series",
"finance_book",
"process_deferred_accounting",
"reversal_of",
"tax_withholding_category",
"column_break1",
@@ -524,13 +525,20 @@
"label": "Reversal Of",
"options": "Journal Entry",
"read_only": 1
},
{
"fieldname": "process_deferred_accounting",
"fieldtype": "Link",
"label": "Process Deferred Accounting",
"options": "Process Deferred Accounting",
"read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 176,
"is_submittable": 1,
"links": [],
"modified": "2022-01-04 13:39:36.485954",
"modified": "2022-04-06 17:18:46.865259",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",
@@ -578,6 +586,7 @@
"search_fields": "voucher_type,posting_date, due_date, cheque_no",
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "title",
"track_changes": 1
}

File diff suppressed because it is too large Load Diff

View File

@@ -39,14 +39,25 @@ class TestJournalEntry(unittest.TestCase):
test_voucher.submit()
if test_voucher.doctype == "Journal Entry":
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
self.assertTrue(
frappe.db.sql(
"""select name from `tabJournal Entry Account`
where account = %s and docstatus = 1 and parent = %s""",
("_Test Receivable - _TC", test_voucher.name)))
("_Test Receivable - _TC", test_voucher.name),
)
)
self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
where reference_type = %s and reference_name = %s""", (test_voucher.doctype, test_voucher.name)))
self.assertFalse(
frappe.db.sql(
"""select name from `tabJournal Entry Account`
where reference_type = %s and reference_name = %s""",
(test_voucher.doctype, test_voucher.name),
)
)
base_jv.get("accounts")[0].is_advance = "Yes" if (test_voucher.doctype in ["Sales Order", "Purchase Order"]) else "No"
base_jv.get("accounts")[0].is_advance = (
"Yes" if (test_voucher.doctype in ["Sales Order", "Purchase Order"]) else "No"
)
base_jv.get("accounts")[0].set("reference_type", test_voucher.doctype)
base_jv.get("accounts")[0].set("reference_name", test_voucher.name)
base_jv.insert()
@@ -54,18 +65,28 @@ class TestJournalEntry(unittest.TestCase):
submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name)
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
where reference_type = %s and reference_name = %s and {0}=400""".format(dr_or_cr),
(submitted_voucher.doctype, submitted_voucher.name)))
self.assertTrue(
frappe.db.sql(
"""select name from `tabJournal Entry Account`
where reference_type = %s and reference_name = %s and {0}=400""".format(
dr_or_cr
),
(submitted_voucher.doctype, submitted_voucher.name),
)
)
if base_jv.get("accounts")[0].is_advance == "Yes":
self.advance_paid_testcase(base_jv, submitted_voucher, dr_or_cr)
self.cancel_against_voucher_testcase(submitted_voucher)
def advance_paid_testcase(self, base_jv, test_voucher, dr_or_cr):
#Test advance paid field
advance_paid = frappe.db.sql("""select advance_paid from `tab%s`
where name=%s""" % (test_voucher.doctype, '%s'), (test_voucher.name))
# Test advance paid field
advance_paid = frappe.db.sql(
"""select advance_paid from `tab%s`
where name=%s"""
% (test_voucher.doctype, "%s"),
(test_voucher.name),
)
payment_against_order = base_jv.get("accounts")[0].get(dr_or_cr)
self.assertTrue(flt(advance_paid[0][0]) == flt(payment_against_order))
@@ -74,13 +95,19 @@ class TestJournalEntry(unittest.TestCase):
if test_voucher.doctype == "Journal Entry":
# if test_voucher is a Journal Entry, test cancellation of test_voucher
test_voucher.cancel()
self.assertFalse(frappe.db.sql("""select name from `tabJournal Entry Account`
where reference_type='Journal Entry' and reference_name=%s""", test_voucher.name))
self.assertFalse(
frappe.db.sql(
"""select name from `tabJournal Entry Account`
where reference_type='Journal Entry' and reference_name=%s""",
test_voucher.name,
)
)
elif test_voucher.doctype in ["Sales Order", "Purchase Order"]:
# if test_voucher is a Sales Order/Purchase Order, test error on cancellation of test_voucher
frappe.db.set_value("Accounts Settings", "Accounts Settings",
"unlink_advance_payment_on_cancelation_of_order", 0)
frappe.db.set_value(
"Accounts Settings", "Accounts Settings", "unlink_advance_payment_on_cancelation_of_order", 0
)
submitted_voucher = frappe.get_doc(test_voucher.doctype, test_voucher.name)
self.assertRaises(frappe.LinkExistsError, submitted_voucher.cancel)
@@ -89,7 +116,10 @@ class TestJournalEntry(unittest.TestCase):
stock_account = get_inventory_account(company)
from erpnext.accounts.utils import get_stock_and_account_balance
account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(stock_account, nowdate(), company)
account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(
stock_account, nowdate(), company
)
diff = flt(account_bal) - flt(stock_bal)
if not diff:
@@ -98,19 +128,25 @@ class TestJournalEntry(unittest.TestCase):
jv = frappe.new_doc("Journal Entry")
jv.company = company
jv.posting_date = nowdate()
jv.append("accounts", {
"account": stock_account,
"cost_center": "Main - TCP1",
"debit_in_account_currency": 0 if diff > 0 else abs(diff),
"credit_in_account_currency": diff if diff > 0 else 0
})
jv.append(
"accounts",
{
"account": stock_account,
"cost_center": "Main - TCP1",
"debit_in_account_currency": 0 if diff > 0 else abs(diff),
"credit_in_account_currency": diff if diff > 0 else 0,
},
)
jv.append("accounts", {
"account": "Stock Adjustment - TCP1",
"cost_center": "Main - TCP1",
"debit_in_account_currency": diff if diff > 0 else 0,
"credit_in_account_currency": 0 if diff > 0 else abs(diff)
})
jv.append(
"accounts",
{
"account": "Stock Adjustment - TCP1",
"cost_center": "Main - TCP1",
"debit_in_account_currency": diff if diff > 0 else 0,
"credit_in_account_currency": 0 if diff > 0 else abs(diff),
},
)
jv.insert()
if account_bal == stock_bal:
@@ -121,16 +157,21 @@ class TestJournalEntry(unittest.TestCase):
jv.cancel()
def test_multi_currency(self):
jv = make_journal_entry("_Test Bank USD - _TC",
"_Test Bank - _TC", 100, exchange_rate=50, save=False)
jv = make_journal_entry(
"_Test Bank USD - _TC", "_Test Bank - _TC", 100, exchange_rate=50, save=False
)
jv.get("accounts")[1].credit_in_account_currency = 5000
jv.submit()
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
gl_entries = frappe.db.sql(
"""select account, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
order by account asc""", jv.name, as_dict=1)
order by account asc""",
jv.name,
as_dict=1,
)
self.assertTrue(gl_entries)
@@ -140,33 +181,42 @@ class TestJournalEntry(unittest.TestCase):
"debit": 5000,
"debit_in_account_currency": 100,
"credit": 0,
"credit_in_account_currency": 0
"credit_in_account_currency": 0,
},
"_Test Bank - _TC": {
"account_currency": "INR",
"debit": 0,
"debit_in_account_currency": 0,
"credit": 5000,
"credit_in_account_currency": 5000
}
"credit_in_account_currency": 5000,
},
}
for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
for field in (
"account_currency",
"debit",
"debit_in_account_currency",
"credit",
"credit_in_account_currency",
):
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account][field], gle[field])
# cancel
jv.cancel()
gle = frappe.db.sql("""select name from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", jv.name)
gle = frappe.db.sql(
"""select name from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""",
jv.name,
)
self.assertFalse(gle)
def test_reverse_journal_entry(self):
from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
jv = make_journal_entry("_Test Bank USD - _TC",
"Sales - _TC", 100, exchange_rate=50, save=False)
jv = make_journal_entry("_Test Bank USD - _TC", "Sales - _TC", 100, exchange_rate=50, save=False)
jv.get("accounts")[1].credit_in_account_currency = 5000
jv.get("accounts")[1].exchange_rate = 1
@@ -176,15 +226,17 @@ class TestJournalEntry(unittest.TestCase):
rjv.posting_date = nowdate()
rjv.submit()
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
gl_entries = frappe.db.sql(
"""select account, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
order by account asc""", rjv.name, as_dict=1)
order by account asc""",
rjv.name,
as_dict=1,
)
self.assertTrue(gl_entries)
expected_values = {
"_Test Bank USD - _TC": {
"account_currency": "USD",
@@ -199,44 +251,38 @@ class TestJournalEntry(unittest.TestCase):
"debit_in_account_currency": 5000,
"credit": 0,
"credit_in_account_currency": 0,
}
},
}
for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
for field in (
"account_currency",
"debit",
"debit_in_account_currency",
"credit",
"credit_in_account_currency",
):
for i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account][field], gle[field])
def test_disallow_change_in_account_currency_for_a_party(self):
# create jv in USD
jv = make_journal_entry("_Test Bank USD - _TC",
"_Test Receivable USD - _TC", 100, save=False)
jv = make_journal_entry("_Test Bank USD - _TC", "_Test Receivable USD - _TC", 100, save=False)
jv.accounts[1].update({
"party_type": "Customer",
"party": "_Test Customer USD"
})
jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer USD"})
jv.submit()
# create jv in USD, but account currency in INR
jv = make_journal_entry("_Test Bank - _TC",
"_Test Receivable - _TC", 100, save=False)
jv = make_journal_entry("_Test Bank - _TC", "_Test Receivable - _TC", 100, save=False)
jv.accounts[1].update({
"party_type": "Customer",
"party": "_Test Customer USD"
})
jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer USD"})
self.assertRaises(InvalidAccountCurrency, jv.submit)
# back in USD
jv = make_journal_entry("_Test Bank USD - _TC",
"_Test Receivable USD - _TC", 100, save=False)
jv = make_journal_entry("_Test Bank USD - _TC", "_Test Receivable USD - _TC", 100, save=False)
jv.accounts[1].update({
"party_type": "Customer",
"party": "_Test Customer USD"
})
jv.accounts[1].update({"party_type": "Customer", "party": "_Test Customer USD"})
jv.submit()
@@ -245,13 +291,27 @@ class TestJournalEntry(unittest.TestCase):
frappe.db.set_value("Account", "Buildings - _TC", "inter_company_account", 1)
frappe.db.set_value("Account", "Sales Expenses - _TC1", "inter_company_account", 1)
frappe.db.set_value("Account", "Buildings - _TC1", "inter_company_account", 1)
jv = make_journal_entry("Sales Expenses - _TC", "Buildings - _TC", 100, posting_date=nowdate(), cost_center = "Main - _TC", save=False)
jv = make_journal_entry(
"Sales Expenses - _TC",
"Buildings - _TC",
100,
posting_date=nowdate(),
cost_center="Main - _TC",
save=False,
)
jv.voucher_type = "Inter Company Journal Entry"
jv.multi_currency = 0
jv.insert()
jv.submit()
jv1 = make_journal_entry("Sales Expenses - _TC1", "Buildings - _TC1", 100, posting_date=nowdate(), cost_center = "Main - _TC1", save=False)
jv1 = make_journal_entry(
"Sales Expenses - _TC1",
"Buildings - _TC1",
100,
posting_date=nowdate(),
cost_center="Main - _TC1",
save=False,
)
jv1.inter_company_journal_entry_reference = jv.name
jv1.company = "_Test Company 1"
jv1.voucher_type = "Inter Company Journal Entry"
@@ -273,9 +333,12 @@ class TestJournalEntry(unittest.TestCase):
def test_jv_with_cost_centre(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False)
jv = make_journal_entry(
"_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center=cost_center, save=False
)
jv.voucher_type = "Bank Entry"
jv.multi_currency = 0
jv.cheque_no = "112233"
@@ -284,17 +347,17 @@ class TestJournalEntry(unittest.TestCase):
jv.submit()
expected_values = {
"_Test Cash - _TC": {
"cost_center": cost_center
},
"_Test Bank - _TC": {
"cost_center": cost_center
}
"_Test Cash - _TC": {"cost_center": cost_center},
"_Test Bank - _TC": {"cost_center": cost_center},
}
gl_entries = frappe.db.sql("""select account, cost_center, debit, credit
gl_entries = frappe.db.sql(
"""select account, cost_center, debit, credit
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
order by account asc""", jv.name, as_dict=1)
order by account asc""",
jv.name,
as_dict=1,
)
self.assertTrue(gl_entries)
@@ -305,11 +368,13 @@ class TestJournalEntry(unittest.TestCase):
from erpnext.projects.doctype.project.test_project import make_project
if not frappe.db.exists("Project", {"project_name": "Journal Entry Project"}):
project = make_project({
'project_name': 'Journal Entry Project',
'project_template_name': 'Test Project Template',
'start_date': '2020-01-01'
})
project = make_project(
{
"project_name": "Journal Entry Project",
"project_template_name": "Test Project Template",
"start_date": "2020-01-01",
}
)
project_name = project.name
else:
project_name = frappe.get_value("Project", {"project_name": "_Test Project"})
@@ -325,17 +390,17 @@ class TestJournalEntry(unittest.TestCase):
jv.submit()
expected_values = {
"_Test Cash - _TC": {
"project": project_name
},
"_Test Bank - _TC": {
"project": project_name
}
"_Test Cash - _TC": {"project": project_name},
"_Test Bank - _TC": {"project": project_name},
}
gl_entries = frappe.db.sql("""select account, project, debit, credit
gl_entries = frappe.db.sql(
"""select account, project, debit, credit
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
order by account asc""", jv.name, as_dict=1)
order by account asc""",
jv.name,
as_dict=1,
)
self.assertTrue(gl_entries)
@@ -345,9 +410,12 @@ class TestJournalEntry(unittest.TestCase):
def test_jv_account_and_party_balance_with_cost_centre(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.utils import get_balance_on
cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center = cost_center, save=False)
jv = make_journal_entry(
"_Test Cash - _TC", "_Test Bank - _TC", 100, cost_center=cost_center, save=False
)
account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center)
jv.voucher_type = "Bank Entry"
jv.multi_currency = 0
@@ -360,7 +428,18 @@ class TestJournalEntry(unittest.TestCase):
account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center)
self.assertEqual(expected_account_balance, account_balance)
def make_journal_entry(account1, account2, amount, cost_center=None, posting_date=None, exchange_rate=1, save=True, submit=False, project=None):
def make_journal_entry(
account1,
account2,
amount,
cost_center=None,
posting_date=None,
exchange_rate=1,
save=True,
submit=False,
project=None,
):
if not cost_center:
cost_center = "_Test Cost Center - _TC"
@@ -369,23 +448,27 @@ def make_journal_entry(account1, account2, amount, cost_center=None, posting_dat
jv.company = "_Test Company"
jv.user_remark = "test"
jv.multi_currency = 1
jv.set("accounts", [
{
"account": account1,
"cost_center": cost_center,
"project": project,
"debit_in_account_currency": amount if amount > 0 else 0,
"credit_in_account_currency": abs(amount) if amount < 0 else 0,
"exchange_rate": exchange_rate
}, {
"account": account2,
"cost_center": cost_center,
"project": project,
"credit_in_account_currency": amount if amount > 0 else 0,
"debit_in_account_currency": abs(amount) if amount < 0 else 0,
"exchange_rate": exchange_rate
}
])
jv.set(
"accounts",
[
{
"account": account1,
"cost_center": cost_center,
"project": project,
"debit_in_account_currency": amount if amount > 0 else 0,
"credit_in_account_currency": abs(amount) if amount < 0 else 0,
"exchange_rate": exchange_rate,
},
{
"account": account2,
"cost_center": cost_center,
"project": project,
"credit_in_account_currency": amount if amount > 0 else 0,
"debit_in_account_currency": abs(amount) if amount < 0 else 0,
"exchange_rate": exchange_rate,
},
],
)
if save or submit:
jv.insert()
@@ -394,4 +477,5 @@ def make_journal_entry(account1, account2, amount, cost_center=None, posting_dat
return jv
test_records = frappe.get_test_records('Journal Entry')
test_records = frappe.get_test_records("Journal Entry")

View File

@@ -9,6 +9,7 @@ from frappe.model.document import Document
class JournalEntryTemplate(Document):
pass
@frappe.whitelist()
def get_naming_series():
return frappe.get_meta("Journal Entry").get_field("naming_series").options

View File

@@ -8,6 +8,7 @@ from frappe.utils import today
exclude_from_linked_with = True
class LoyaltyPointEntry(Document):
pass
@@ -16,18 +17,28 @@ def get_loyalty_point_entries(customer, loyalty_program, company, expiry_date=No
if not expiry_date:
expiry_date = today()
return frappe.db.sql('''
return frappe.db.sql(
"""
select name, loyalty_points, expiry_date, loyalty_program_tier, invoice_type, invoice
from `tabLoyalty Point Entry`
where customer=%s and loyalty_program=%s
and expiry_date>=%s and loyalty_points>0 and company=%s
order by expiry_date
''', (customer, loyalty_program, expiry_date, company), as_dict=1)
""",
(customer, loyalty_program, expiry_date, company),
as_dict=1,
)
def get_redemption_details(customer, loyalty_program, company):
return frappe._dict(frappe.db.sql('''
return frappe._dict(
frappe.db.sql(
"""
select redeem_against, sum(loyalty_points)
from `tabLoyalty Point Entry`
where customer=%s and loyalty_program=%s and loyalty_points<0 and company=%s
group by redeem_against
''', (customer, loyalty_program, company)))
""",
(customer, loyalty_program, company),
)
)

View File

@@ -12,39 +12,61 @@ class LoyaltyProgram(Document):
pass
def get_loyalty_details(customer, loyalty_program, expiry_date=None, company=None, include_expired_entry=False):
def get_loyalty_details(
customer, loyalty_program, expiry_date=None, company=None, include_expired_entry=False
):
if not expiry_date:
expiry_date = today()
condition = ''
condition = ""
if company:
condition = " and company=%s " % frappe.db.escape(company)
if not include_expired_entry:
condition += " and expiry_date>='%s' " % expiry_date
loyalty_point_details = frappe.db.sql('''select sum(loyalty_points) as loyalty_points,
loyalty_point_details = frappe.db.sql(
"""select sum(loyalty_points) as loyalty_points,
sum(purchase_amount) as total_spent from `tabLoyalty Point Entry`
where customer=%s and loyalty_program=%s and posting_date <= %s
{condition}
group by customer'''.format(condition=condition),
(customer, loyalty_program, expiry_date), as_dict=1)
group by customer""".format(
condition=condition
),
(customer, loyalty_program, expiry_date),
as_dict=1,
)
if loyalty_point_details:
return loyalty_point_details[0]
else:
return {"loyalty_points": 0, "total_spent": 0}
@frappe.whitelist()
def get_loyalty_program_details_with_points(customer, loyalty_program=None, expiry_date=None, company=None, \
silent=False, include_expired_entry=False, current_transaction_amount=0):
lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent)
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
lp_details.update(get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry))
tier_spent_level = sorted([d.as_dict() for d in loyalty_program.collection_rules],
key=lambda rule:rule.min_spent, reverse=True)
@frappe.whitelist()
def get_loyalty_program_details_with_points(
customer,
loyalty_program=None,
expiry_date=None,
company=None,
silent=False,
include_expired_entry=False,
current_transaction_amount=0,
):
lp_details = get_loyalty_program_details(
customer, loyalty_program, company=company, silent=silent
)
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
lp_details.update(
get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry)
)
tier_spent_level = sorted(
[d.as_dict() for d in loyalty_program.collection_rules],
key=lambda rule: rule.min_spent,
reverse=True,
)
for i, d in enumerate(tier_spent_level):
if i==0 or (lp_details.total_spent+current_transaction_amount) <= d.min_spent:
if i == 0 or (lp_details.total_spent + current_transaction_amount) <= d.min_spent:
lp_details.tier_name = d.tier_name
lp_details.collection_factor = d.collection_factor
else:
@@ -52,8 +74,16 @@ def get_loyalty_program_details_with_points(customer, loyalty_program=None, expi
return lp_details
@frappe.whitelist()
def get_loyalty_program_details(customer, loyalty_program=None, expiry_date=None, company=None, silent=False, include_expired_entry=False):
def get_loyalty_program_details(
customer,
loyalty_program=None,
expiry_date=None,
company=None,
silent=False,
include_expired_entry=False,
):
lp_details = frappe._dict()
if not loyalty_program:
@@ -72,6 +102,7 @@ def get_loyalty_program_details(customer, loyalty_program=None, expiry_date=None
lp_details.update(loyalty_program.as_dict())
return lp_details
@frappe.whitelist()
def get_redeemption_factor(loyalty_program=None, customer=None):
customer_loyalty_program = None
@@ -98,13 +129,16 @@ def validate_loyalty_points(ref_doc, points_to_redeem):
else:
loyalty_program = frappe.db.get_value("Customer", ref_doc.customer, ["loyalty_program"])
if loyalty_program and frappe.db.get_value("Loyalty Program", loyalty_program, ["company"]) !=\
ref_doc.company:
if (
loyalty_program
and frappe.db.get_value("Loyalty Program", loyalty_program, ["company"]) != ref_doc.company
):
frappe.throw(_("The Loyalty Program isn't valid for the selected company"))
if loyalty_program and points_to_redeem:
loyalty_program_details = get_loyalty_program_details_with_points(ref_doc.customer, loyalty_program,
posting_date, ref_doc.company)
loyalty_program_details = get_loyalty_program_details_with_points(
ref_doc.customer, loyalty_program, posting_date, ref_doc.company
)
if points_to_redeem > loyalty_program_details.loyalty_points:
frappe.throw(_("You don't have enought Loyalty Points to redeem"))

View File

@@ -1,11 +1,5 @@
def get_data():
return {
'fieldname': 'loyalty_program',
'transactions': [
{
'items': ['Sales Invoice', 'Customer']
}
]
"fieldname": "loyalty_program",
"transactions": [{"items": ["Sales Invoice", "Customer"]}],
}

View File

@@ -19,19 +19,28 @@ class TestLoyaltyProgram(unittest.TestCase):
create_records()
def test_loyalty_points_earned_single_tier(self):
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
frappe.db.set_value(
"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
)
# create a new sales invoice
si_original = create_sales_invoice_record()
si_original.insert()
si_original.submit()
customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"})
customer = frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"})
earned_points = get_points_earned(si_original)
lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer})
lpe = frappe.get_doc(
"Loyalty Point Entry",
{
"invoice_type": "Sales Invoice",
"invoice": si_original.name,
"customer": si_original.customer,
},
)
self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program)
self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier)
self.assertEqual(si_original.get("loyalty_program"), customer.loyalty_program)
self.assertEqual(lpe.get("loyalty_program_tier"), customer.loyalty_program_tier)
self.assertEqual(lpe.loyalty_points, earned_points)
# add redemption point
@@ -43,21 +52,31 @@ class TestLoyaltyProgram(unittest.TestCase):
earned_after_redemption = get_points_earned(si_redeem)
lpe_redeem = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'redeem_against': lpe.name})
lpe_earn = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'name': ['!=', lpe_redeem.name]})
lpe_redeem = frappe.get_doc(
"Loyalty Point Entry",
{"invoice_type": "Sales Invoice", "invoice": si_redeem.name, "redeem_against": lpe.name},
)
lpe_earn = frappe.get_doc(
"Loyalty Point Entry",
{"invoice_type": "Sales Invoice", "invoice": si_redeem.name, "name": ["!=", lpe_redeem.name]},
)
self.assertEqual(lpe_earn.loyalty_points, earned_after_redemption)
self.assertEqual(lpe_redeem.loyalty_points, (-1*earned_points))
self.assertEqual(lpe_redeem.loyalty_points, (-1 * earned_points))
# cancel and delete
for d in [si_redeem, si_original]:
d.cancel()
def test_loyalty_points_earned_multiple_tier(self):
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Multiple Loyalty")
frappe.db.set_value(
"Customer", "Test Loyalty Customer", "loyalty_program", "Test Multiple Loyalty"
)
# assign multiple tier program to the customer
customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"})
customer.loyalty_program = frappe.get_doc('Loyalty Program', {'loyalty_program_name': 'Test Multiple Loyalty'}).name
customer = frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"})
customer.loyalty_program = frappe.get_doc(
"Loyalty Program", {"loyalty_program_name": "Test Multiple Loyalty"}
).name
customer.save()
# create a new sales invoice
@@ -67,10 +86,17 @@ class TestLoyaltyProgram(unittest.TestCase):
earned_points = get_points_earned(si_original)
lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer})
lpe = frappe.get_doc(
"Loyalty Point Entry",
{
"invoice_type": "Sales Invoice",
"invoice": si_original.name,
"customer": si_original.customer,
},
)
self.assertEqual(si_original.get('loyalty_program'), customer.loyalty_program)
self.assertEqual(lpe.get('loyalty_program_tier'), customer.loyalty_program_tier)
self.assertEqual(si_original.get("loyalty_program"), customer.loyalty_program)
self.assertEqual(lpe.get("loyalty_program_tier"), customer.loyalty_program_tier)
self.assertEqual(lpe.loyalty_points, earned_points)
# add redemption point
@@ -80,14 +106,20 @@ class TestLoyaltyProgram(unittest.TestCase):
si_redeem.insert()
si_redeem.submit()
customer = frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"})
customer = frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"})
earned_after_redemption = get_points_earned(si_redeem)
lpe_redeem = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'redeem_against': lpe.name})
lpe_earn = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_redeem.name, 'name': ['!=', lpe_redeem.name]})
lpe_redeem = frappe.get_doc(
"Loyalty Point Entry",
{"invoice_type": "Sales Invoice", "invoice": si_redeem.name, "redeem_against": lpe.name},
)
lpe_earn = frappe.get_doc(
"Loyalty Point Entry",
{"invoice_type": "Sales Invoice", "invoice": si_redeem.name, "name": ["!=", lpe_redeem.name]},
)
self.assertEqual(lpe_earn.loyalty_points, earned_after_redemption)
self.assertEqual(lpe_redeem.loyalty_points, (-1*earned_points))
self.assertEqual(lpe_redeem.loyalty_points, (-1 * earned_points))
self.assertEqual(lpe_earn.loyalty_program_tier, customer.loyalty_program_tier)
# cancel and delete
@@ -95,23 +127,30 @@ class TestLoyaltyProgram(unittest.TestCase):
d.cancel()
def test_cancel_sales_invoice(self):
''' cancelling the sales invoice should cancel the earned points'''
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
"""cancelling the sales invoice should cancel the earned points"""
frappe.db.set_value(
"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
)
# create a new sales invoice
si = create_sales_invoice_record()
si.insert()
si.submit()
lpe = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si.name, 'customer': si.customer})
lpe = frappe.get_doc(
"Loyalty Point Entry",
{"invoice_type": "Sales Invoice", "invoice": si.name, "customer": si.customer},
)
self.assertEqual(True, not (lpe is None))
# cancelling sales invoice
si.cancel()
lpe = frappe.db.exists('Loyalty Point Entry', lpe.name)
lpe = frappe.db.exists("Loyalty Point Entry", lpe.name)
self.assertEqual(True, (lpe is None))
def test_sales_invoice_return(self):
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
frappe.db.set_value(
"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
)
# create a new sales invoice
si_original = create_sales_invoice_record(2)
si_original.conversion_rate = flt(1)
@@ -119,7 +158,14 @@ class TestLoyaltyProgram(unittest.TestCase):
si_original.submit()
earned_points = get_points_earned(si_original)
lpe_original = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer})
lpe_original = frappe.get_doc(
"Loyalty Point Entry",
{
"invoice_type": "Sales Invoice",
"invoice": si_original.name,
"customer": si_original.customer,
},
)
self.assertEqual(lpe_original.loyalty_points, earned_points)
# create sales invoice return
@@ -131,10 +177,17 @@ class TestLoyaltyProgram(unittest.TestCase):
si_return.submit()
# fetch original invoice again as its status would have been updated
si_original = frappe.get_doc('Sales Invoice', lpe_original.invoice)
si_original = frappe.get_doc("Sales Invoice", lpe_original.invoice)
earned_points = get_points_earned(si_original)
lpe_after_return = frappe.get_doc('Loyalty Point Entry', {'invoice_type': 'Sales Invoice', 'invoice': si_original.name, 'customer': si_original.customer})
lpe_after_return = frappe.get_doc(
"Loyalty Point Entry",
{
"invoice_type": "Sales Invoice",
"invoice": si_original.name,
"customer": si_original.customer,
},
)
self.assertEqual(lpe_after_return.loyalty_points, earned_points)
self.assertEqual(True, (lpe_original.loyalty_points > lpe_after_return.loyalty_points))
@@ -143,144 +196,164 @@ class TestLoyaltyProgram(unittest.TestCase):
try:
d.cancel()
except frappe.TimestampMismatchError:
frappe.get_doc('Sales Invoice', d.name).cancel()
frappe.get_doc("Sales Invoice", d.name).cancel()
def test_loyalty_points_for_dashboard(self):
doc = frappe.get_doc('Customer', 'Test Loyalty Customer')
doc = frappe.get_doc("Customer", "Test Loyalty Customer")
company_wise_info = get_dashboard_info("Customer", doc.name, doc.loyalty_program)
for d in company_wise_info:
self.assertTrue(d.get("loyalty_points"))
def get_points_earned(self):
def get_returned_amount():
returned_amount = frappe.db.sql("""
returned_amount = frappe.db.sql(
"""
select sum(grand_total)
from `tabSales Invoice`
where docstatus=1 and is_return=1 and ifnull(return_against, '')=%s
""", self.name)
""",
self.name,
)
return abs(flt(returned_amount[0][0])) if returned_amount else 0
lp_details = get_loyalty_program_details_with_points(self.customer, company=self.company,
loyalty_program=self.loyalty_program, expiry_date=self.posting_date, include_expired_entry=True)
if lp_details and getdate(lp_details.from_date) <= getdate(self.posting_date) and \
(not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date)):
lp_details = get_loyalty_program_details_with_points(
self.customer,
company=self.company,
loyalty_program=self.loyalty_program,
expiry_date=self.posting_date,
include_expired_entry=True,
)
if (
lp_details
and getdate(lp_details.from_date) <= getdate(self.posting_date)
and (not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date))
):
returned_amount = get_returned_amount()
eligible_amount = flt(self.grand_total) - cint(self.loyalty_amount) - returned_amount
points_earned = cint(eligible_amount/lp_details.collection_factor)
points_earned = cint(eligible_amount / lp_details.collection_factor)
return points_earned or 0
def create_sales_invoice_record(qty=1):
# return sales invoice doc object
return frappe.get_doc({
"doctype": "Sales Invoice",
"customer": frappe.get_doc('Customer', {"customer_name": "Test Loyalty Customer"}).name,
"company": '_Test Company',
"due_date": today(),
"posting_date": today(),
"currency": "INR",
"taxes_and_charges": "",
"debit_to": "Debtors - _TC",
"taxes": [],
"items": [{
'doctype': 'Sales Invoice Item',
'item_code': frappe.get_doc('Item', {'item_name': 'Loyal Item'}).name,
'qty': qty,
"rate": 10000,
'income_account': 'Sales - _TC',
'cost_center': 'Main - _TC',
'expense_account': 'Cost of Goods Sold - _TC'
}]
})
return frappe.get_doc(
{
"doctype": "Sales Invoice",
"customer": frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"}).name,
"company": "_Test Company",
"due_date": today(),
"posting_date": today(),
"currency": "INR",
"taxes_and_charges": "",
"debit_to": "Debtors - _TC",
"taxes": [],
"items": [
{
"doctype": "Sales Invoice Item",
"item_code": frappe.get_doc("Item", {"item_name": "Loyal Item"}).name,
"qty": qty,
"rate": 10000,
"income_account": "Sales - _TC",
"cost_center": "Main - _TC",
"expense_account": "Cost of Goods Sold - _TC",
}
],
}
)
def create_records():
# create a new loyalty Account
if not frappe.db.exists("Account", "Loyalty - _TC"):
frappe.get_doc({
"doctype": "Account",
"account_name": "Loyalty",
"parent_account": "Direct Expenses - _TC",
"company": "_Test Company",
"is_group": 0,
"account_type": "Expense Account",
}).insert()
frappe.get_doc(
{
"doctype": "Account",
"account_name": "Loyalty",
"parent_account": "Direct Expenses - _TC",
"company": "_Test Company",
"is_group": 0,
"account_type": "Expense Account",
}
).insert()
# create a new loyalty program Single tier
if not frappe.db.exists("Loyalty Program","Test Single Loyalty"):
frappe.get_doc({
"doctype": "Loyalty Program",
"loyalty_program_name": "Test Single Loyalty",
"auto_opt_in": 1,
"from_date": today(),
"loyalty_program_type": "Single Tier Program",
"conversion_factor": 1,
"expiry_duration": 10,
"company": "_Test Company",
"cost_center": "Main - _TC",
"expense_account": "Loyalty - _TC",
"collection_rules": [{
'tier_name': 'Silver',
'collection_factor': 1000,
'min_spent': 1000
}]
}).insert()
if not frappe.db.exists("Loyalty Program", "Test Single Loyalty"):
frappe.get_doc(
{
"doctype": "Loyalty Program",
"loyalty_program_name": "Test Single Loyalty",
"auto_opt_in": 1,
"from_date": today(),
"loyalty_program_type": "Single Tier Program",
"conversion_factor": 1,
"expiry_duration": 10,
"company": "_Test Company",
"cost_center": "Main - _TC",
"expense_account": "Loyalty - _TC",
"collection_rules": [{"tier_name": "Silver", "collection_factor": 1000, "min_spent": 1000}],
}
).insert()
# create a new customer
if not frappe.db.exists("Customer","Test Loyalty Customer"):
frappe.get_doc({
"customer_group": "_Test Customer Group",
"customer_name": "Test Loyalty Customer",
"customer_type": "Individual",
"doctype": "Customer",
"territory": "_Test Territory"
}).insert()
if not frappe.db.exists("Customer", "Test Loyalty Customer"):
frappe.get_doc(
{
"customer_group": "_Test Customer Group",
"customer_name": "Test Loyalty Customer",
"customer_type": "Individual",
"doctype": "Customer",
"territory": "_Test Territory",
}
).insert()
# create a new loyalty program Multiple tier
if not frappe.db.exists("Loyalty Program","Test Multiple Loyalty"):
frappe.get_doc({
"doctype": "Loyalty Program",
"loyalty_program_name": "Test Multiple Loyalty",
"auto_opt_in": 1,
"from_date": today(),
"loyalty_program_type": "Multiple Tier Program",
"conversion_factor": 1,
"expiry_duration": 10,
"company": "_Test Company",
"cost_center": "Main - _TC",
"expense_account": "Loyalty - _TC",
"collection_rules": [
{
'tier_name': 'Silver',
'collection_factor': 1000,
'min_spent': 10000
},
{
'tier_name': 'Gold',
'collection_factor': 1000,
'min_spent': 19000
}
]
}).insert()
if not frappe.db.exists("Loyalty Program", "Test Multiple Loyalty"):
frappe.get_doc(
{
"doctype": "Loyalty Program",
"loyalty_program_name": "Test Multiple Loyalty",
"auto_opt_in": 1,
"from_date": today(),
"loyalty_program_type": "Multiple Tier Program",
"conversion_factor": 1,
"expiry_duration": 10,
"company": "_Test Company",
"cost_center": "Main - _TC",
"expense_account": "Loyalty - _TC",
"collection_rules": [
{"tier_name": "Silver", "collection_factor": 1000, "min_spent": 10000},
{"tier_name": "Gold", "collection_factor": 1000, "min_spent": 19000},
],
}
).insert()
# create an item
if not frappe.db.exists("Item", "Loyal Item"):
frappe.get_doc({
"doctype": "Item",
"item_code": "Loyal Item",
"item_name": "Loyal Item",
"item_group": "All Item Groups",
"company": "_Test Company",
"is_stock_item": 1,
"opening_stock": 100,
"valuation_rate": 10000,
}).insert()
frappe.get_doc(
{
"doctype": "Item",
"item_code": "Loyal Item",
"item_name": "Loyal Item",
"item_group": "All Item Groups",
"company": "_Test Company",
"is_stock_item": 1,
"opening_stock": 100,
"valuation_rate": 10000,
}
).insert()
# create item price
if not frappe.db.exists("Item Price", {"price_list": "Standard Selling", "item_code": "Loyal Item"}):
frappe.get_doc({
"doctype": "Item Price",
"price_list": "Standard Selling",
"item_code": "Loyal Item",
"price_list_rate": 10000
}).insert()
if not frappe.db.exists(
"Item Price", {"price_list": "Standard Selling", "item_code": "Loyal Item"}
):
frappe.get_doc(
{
"doctype": "Item Price",
"price_list": "Standard Selling",
"item_code": "Loyal Item",
"price_list_rate": 10000,
}
).insert()

View File

@@ -19,23 +19,35 @@ class ModeofPayment(Document):
for entry in self.accounts:
accounts_list.append(entry.company)
if len(accounts_list)!= len(set(accounts_list)):
if len(accounts_list) != len(set(accounts_list)):
frappe.throw(_("Same Company is entered more than once"))
def validate_accounts(self):
for entry in self.accounts:
"""Error when Company of Ledger account doesn't match with Company Selected"""
if frappe.db.get_value("Account", entry.default_account, "company") != entry.company:
frappe.throw(_("Account {0} does not match with Company {1} in Mode of Account: {2}")
.format(entry.default_account, entry.company, self.name))
frappe.throw(
_("Account {0} does not match with Company {1} in Mode of Account: {2}").format(
entry.default_account, entry.company, self.name
)
)
def validate_pos_mode_of_payment(self):
if not self.enabled:
pos_profiles = frappe.db.sql("""SELECT sip.parent FROM `tabSales Invoice Payment` sip
WHERE sip.parenttype = 'POS Profile' and sip.mode_of_payment = %s""", (self.name))
pos_profiles = frappe.db.sql(
"""SELECT sip.parent FROM `tabSales Invoice Payment` sip
WHERE sip.parenttype = 'POS Profile' and sip.mode_of_payment = %s""",
(self.name),
)
pos_profiles = list(map(lambda x: x[0], pos_profiles))
if pos_profiles:
message = "POS Profile " + frappe.bold(", ".join(pos_profiles)) + " contains \
Mode of Payment " + frappe.bold(str(self.name)) + ". Please remove them to disable this mode."
message = (
"POS Profile "
+ frappe.bold(", ".join(pos_profiles))
+ " contains \
Mode of Payment "
+ frappe.bold(str(self.name))
+ ". Please remove them to disable this mode."
)
frappe.throw(_(message), title="Not Allowed")

View File

@@ -5,5 +5,6 @@ import unittest
# test_records = frappe.get_test_records('Mode of Payment')
class TestModeofPayment(unittest.TestCase):
pass

View File

@@ -11,13 +11,25 @@ from frappe.utils import add_months, flt
class MonthlyDistribution(Document):
@frappe.whitelist()
def get_months(self):
month_list = ['January','February','March','April','May','June','July','August','September',
'October','November','December']
idx =1
month_list = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
]
idx = 1
for m in month_list:
mnth = self.append('percentages')
mnth = self.append("percentages")
mnth.month = m
mnth.percentage_allocation = 100.0/12
mnth.percentage_allocation = 100.0 / 12
mnth.idx = idx
idx += 1
@@ -25,18 +37,15 @@ class MonthlyDistribution(Document):
total = sum(flt(d.percentage_allocation) for d in self.get("percentages"))
if flt(total, 2) != 100.0:
frappe.throw(_("Percentage Allocation should be equal to 100%") + \
" ({0}%)".format(str(flt(total, 2))))
frappe.throw(
_("Percentage Allocation should be equal to 100%") + " ({0}%)".format(str(flt(total, 2)))
)
def get_periodwise_distribution_data(distribution_id, period_list, periodicity):
doc = frappe.get_doc('Monthly Distribution', distribution_id)
doc = frappe.get_doc("Monthly Distribution", distribution_id)
months_to_add = {
"Yearly": 12,
"Half-Yearly": 6,
"Quarterly": 3,
"Monthly": 1
}[periodicity]
months_to_add = {"Yearly": 12, "Half-Yearly": 6, "Quarterly": 3, "Monthly": 1}[periodicity]
period_dict = {}
@@ -45,6 +54,7 @@ def get_periodwise_distribution_data(distribution_id, period_list, periodicity):
return period_dict
def get_percentage(doc, start_date, period):
percentage = 0
months = [start_date.strftime("%B").title()]

View File

@@ -1,22 +1,16 @@
from frappe import _
def get_data():
return {
'fieldname': 'monthly_distribution',
'non_standard_fieldnames': {
'Sales Person': 'distribution_id',
'Territory': 'distribution_id',
'Sales Partner': 'distribution_id',
"fieldname": "monthly_distribution",
"non_standard_fieldnames": {
"Sales Person": "distribution_id",
"Territory": "distribution_id",
"Sales Partner": "distribution_id",
},
'transactions': [
{
'label': _('Target Details'),
'items': ['Sales Person', 'Territory', 'Sales Partner']
},
{
'items': ['Budget']
}
]
"transactions": [
{"label": _("Target Details"), "items": ["Sales Person", "Territory", "Sales Partner"]},
{"items": ["Budget"]},
],
}

View File

@@ -6,7 +6,8 @@ import unittest
import frappe
test_records = frappe.get_test_records('Monthly Distribution')
test_records = frappe.get_test_records("Monthly Distribution")
class TestMonthlyDistribution(unittest.TestCase):
pass

View File

@@ -75,7 +75,7 @@
],
"hide_toolbar": 1,
"issingle": 1,
"modified": "2022-01-04 15:25:06.053187",
"modified": "2022-01-04 16:25:06.053187",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Opening Invoice Creation Tool",

View File

@@ -20,9 +20,9 @@ class OpeningInvoiceCreationTool(Document):
def onload(self):
"""Load the Opening Invoice summary"""
summary, max_count = self.get_opening_invoice_summary()
self.set_onload('opening_invoices_summary', summary)
self.set_onload('max_count', max_count)
self.set_onload('temporary_opening_account', get_temporary_opening_account(self.company))
self.set_onload("opening_invoices_summary", summary)
self.set_onload("max_count", max_count)
self.set_onload("temporary_opening_account", get_temporary_opening_account(self.company))
def get_opening_invoice_summary(self):
def prepare_invoice_summary(doctype, invoices):
@@ -32,10 +32,7 @@ class OpeningInvoiceCreationTool(Document):
for invoice in invoices:
company = invoice.pop("company")
_summary = invoices_summary.get(company, {})
_summary.update({
"currency": company_wise_currency.get(company),
doctype: invoice
})
_summary.update({"currency": company_wise_currency.get(company), doctype: invoice})
invoices_summary.update({company: _summary})
if invoice.paid_amount:
@@ -44,17 +41,21 @@ class OpeningInvoiceCreationTool(Document):
outstanding_amount.append(invoice.outstanding_amount)
if paid_amount or outstanding_amount:
max_count.update({
doctype: {
"max_paid": max(paid_amount) if paid_amount else 0.0,
"max_due": max(outstanding_amount) if outstanding_amount else 0.0
max_count.update(
{
doctype: {
"max_paid": max(paid_amount) if paid_amount else 0.0,
"max_due": max(outstanding_amount) if outstanding_amount else 0.0,
}
}
})
)
invoices_summary = {}
max_count = {}
fields = [
"company", "count(name) as total_invoices", "sum(outstanding_amount) as outstanding_amount"
"company",
"count(name) as total_invoices",
"sum(outstanding_amount) as outstanding_amount",
]
companies = frappe.get_all("Company", fields=["name as company", "default_currency as currency"])
if not companies:
@@ -62,8 +63,9 @@ class OpeningInvoiceCreationTool(Document):
company_wise_currency = {row.company: row.currency for row in companies}
for doctype in ["Sales Invoice", "Purchase Invoice"]:
invoices = frappe.get_all(doctype, filters=dict(is_opening="Yes", docstatus=1),
fields=fields, group_by="company")
invoices = frappe.get_all(
doctype, filters=dict(is_opening="Yes", docstatus=1), fields=fields, group_by="company"
)
prepare_invoice_summary(doctype, invoices)
return invoices_summary, max_count
@@ -74,7 +76,9 @@ class OpeningInvoiceCreationTool(Document):
def set_missing_values(self, row):
row.qty = row.qty or 1.0
row.temporary_opening_account = row.temporary_opening_account or get_temporary_opening_account(self.company)
row.temporary_opening_account = row.temporary_opening_account or get_temporary_opening_account(
self.company
)
row.party_type = "Customer" if self.invoice_type == "Sales" else "Supplier"
row.item_name = row.item_name or _("Opening Invoice Item")
row.posting_date = row.posting_date or nowdate()
@@ -85,7 +89,11 @@ class OpeningInvoiceCreationTool(Document):
if self.create_missing_party:
self.add_party(row.party_type, row.party)
else:
frappe.throw(_("Row #{}: {} {} does not exist.").format(row.idx, frappe.bold(row.party_type), frappe.bold(row.party)))
frappe.throw(
_("Row #{}: {} {} does not exist.").format(
row.idx, frappe.bold(row.party_type), frappe.bold(row.party)
)
)
mandatory_error_msg = _("Row #{0}: {1} is required to create the Opening {2} Invoices")
for d in ("Party", "Outstanding Amount", "Temporary Opening Account"):
@@ -100,12 +108,22 @@ class OpeningInvoiceCreationTool(Document):
self.set_missing_values(row)
self.validate_mandatory_invoice_fields(row)
invoice = self.get_invoice_dict(row)
company_details = frappe.get_cached_value('Company', self.company, ["default_currency", "default_letter_head"], as_dict=1) or {}
company_details = (
frappe.get_cached_value(
"Company", self.company, ["default_currency", "default_letter_head"], as_dict=1
)
or {}
)
default_currency = frappe.db.get_value(row.party_type, row.party, "default_currency")
if company_details:
invoice.update({
"currency": company_details.get("default_currency"),
"letter_head": company_details.get("default_letter_head")
})
invoice.update(
{
"currency": default_currency or company_details.get("default_currency"),
"letter_head": company_details.get("default_letter_head"),
}
)
invoices.append(invoice)
return invoices
@@ -127,55 +145,61 @@ class OpeningInvoiceCreationTool(Document):
def get_invoice_dict(self, row=None):
def get_item_dict():
cost_center = row.get('cost_center') or frappe.get_cached_value('Company', self.company, "cost_center")
cost_center = row.get("cost_center") or frappe.get_cached_value(
"Company", self.company, "cost_center"
)
if not cost_center:
frappe.throw(_("Please set the Default Cost Center in {0} company.").format(frappe.bold(self.company)))
frappe.throw(
_("Please set the Default Cost Center in {0} company.").format(frappe.bold(self.company))
)
income_expense_account_field = "income_account" if row.party_type == "Customer" else "expense_account"
income_expense_account_field = (
"income_account" if row.party_type == "Customer" else "expense_account"
)
default_uom = frappe.db.get_single_value("Stock Settings", "stock_uom") or _("Nos")
rate = flt(row.outstanding_amount) / flt(row.qty)
item_dict = frappe._dict({
"uom": default_uom,
"rate": rate or 0.0,
"qty": row.qty,
"conversion_factor": 1.0,
"item_name": row.item_name or "Opening Invoice Item",
"description": row.item_name or "Opening Invoice Item",
income_expense_account_field: row.temporary_opening_account,
"cost_center": cost_center
})
item_dict = frappe._dict(
{
"uom": default_uom,
"rate": rate or 0.0,
"qty": row.qty,
"conversion_factor": 1.0,
"item_name": row.item_name or "Opening Invoice Item",
"description": row.item_name or "Opening Invoice Item",
income_expense_account_field: row.temporary_opening_account,
"cost_center": cost_center,
}
)
for dimension in get_accounting_dimensions():
item_dict.update({
dimension: row.get(dimension)
})
item_dict.update({dimension: row.get(dimension)})
return item_dict
item = get_item_dict()
invoice = frappe._dict({
"items": [item],
"is_opening": "Yes",
"set_posting_time": 1,
"company": self.company,
"cost_center": self.cost_center,
"due_date": row.due_date,
"posting_date": row.posting_date,
frappe.scrub(row.party_type): row.party,
"is_pos": 0,
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice",
"update_stock": 0,
"invoice_number": row.invoice_number,
"disable_rounded_total": 1
})
invoice = frappe._dict(
{
"items": [item],
"is_opening": "Yes",
"set_posting_time": 1,
"company": self.company,
"cost_center": self.cost_center,
"due_date": row.due_date,
"posting_date": row.posting_date,
frappe.scrub(row.party_type): row.party,
"is_pos": 0,
"doctype": "Sales Invoice" if self.invoice_type == "Sales" else "Purchase Invoice",
"update_stock": 0, # important: https://github.com/frappe/erpnext/pull/23559
"invoice_number": row.invoice_number,
"disable_rounded_total": 1,
}
)
accounting_dimension = get_accounting_dimensions()
for dimension in accounting_dimension:
invoice.update({
dimension: self.get(dimension) or item.get(dimension)
})
invoice.update({dimension: self.get(dimension) or item.get(dimension)})
return invoice
@@ -201,9 +225,10 @@ class OpeningInvoiceCreationTool(Document):
event="opening_invoice_creation",
job_name=self.name,
invoices=invoices,
now=frappe.conf.developer_mode or frappe.flags.in_test
now=frappe.conf.developer_mode or frappe.flags.in_test,
)
def start_import(invoices):
errors = 0
names = []
@@ -222,35 +247,43 @@ def start_import(invoices):
except Exception:
errors += 1
frappe.db.rollback()
message = "\n".join(["Data:", dumps(d, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()])
message = "\n".join(
["Data:", dumps(d, default=str, indent=4), "--" * 50, "\nException:", traceback.format_exc()]
)
frappe.log_error(title="Error while creating Opening Invoice", message=message)
frappe.db.commit()
if errors:
frappe.msgprint(_("You had {} errors while creating opening invoices. Check {} for more details")
.format(errors, "<a href='/app/List/Error Log' class='variant-click'>Error Log</a>"), indicator="red", title=_("Error Occured"))
frappe.msgprint(
_("You had {} errors while creating opening invoices. Check {} for more details").format(
errors, "<a href='/app/List/Error Log' class='variant-click'>Error Log</a>"
),
indicator="red",
title=_("Error Occured"),
)
return names
def publish(index, total, doctype):
if total < 5: return
if total < 5:
return
frappe.publish_realtime(
"opening_invoice_creation_progress",
dict(
title=_("Opening Invoice Creation In Progress"),
message=_('Creating {} out of {} {}').format(index + 1, total, doctype),
message=_("Creating {} out of {} {}").format(index + 1, total, doctype),
user=frappe.session.user,
count=index+1,
total=total
))
count=index + 1,
total=total,
),
)
@frappe.whitelist()
def get_temporary_opening_account(company=None):
if not company:
return
accounts = frappe.get_all("Account", filters={
'company': company,
'account_type': 'Temporary'
})
accounts = frappe.get_all("Account", filters={"company": company, "account_type": "Temporary"})
if not accounts:
frappe.throw(_("Please add a Temporary Opening account in Chart of Accounts"))

View File

@@ -1,11 +1,8 @@
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
import unittest
import frappe
from frappe.cache_manager import clear_doctype_cache
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from frappe.tests.utils import FrappeTestCase
from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import (
create_dimension,
@@ -17,40 +14,51 @@ from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_crea
test_dependencies = ["Customer", "Supplier", "Accounting Dimension"]
class TestOpeningInvoiceCreationTool(unittest.TestCase):
def setUp(self):
class TestOpeningInvoiceCreationTool(FrappeTestCase):
@classmethod
def setUpClass(self):
if not frappe.db.exists("Company", "_Test Opening Invoice Company"):
make_company()
create_dimension()
return super().setUpClass()
def make_invoices(self, invoice_type="Sales", company=None, party_1=None, party_2=None, invoice_number=None, department=None):
def make_invoices(
self,
invoice_type="Sales",
company=None,
party_1=None,
party_2=None,
invoice_number=None,
department=None,
):
doc = frappe.get_single("Opening Invoice Creation Tool")
args = get_opening_invoice_creation_dict(invoice_type=invoice_type, company=company,
party_1=party_1, party_2=party_2, invoice_number=invoice_number, department=department)
args = get_opening_invoice_creation_dict(
invoice_type=invoice_type,
company=company,
party_1=party_1,
party_2=party_2,
invoice_number=invoice_number,
department=department,
)
doc.update(args)
return doc.make_invoices()
def test_opening_sales_invoice_creation(self):
property_setter = make_property_setter("Sales Invoice", "update_stock", "default", 1, "Check")
try:
invoices = self.make_invoices(company="_Test Opening Invoice Company")
invoices = self.make_invoices(company="_Test Opening Invoice Company")
self.assertEqual(len(invoices), 2)
expected_value = {
"keys": ["customer", "outstanding_amount", "status"],
0: ["_Test Customer", 300, "Overdue"],
1: ["_Test Customer 1", 250, "Overdue"],
}
self.check_expected_values(invoices, expected_value)
self.assertEqual(len(invoices), 2)
expected_value = {
"keys": ["customer", "outstanding_amount", "status"],
0: ["_Test Customer", 300, "Overdue"],
1: ["_Test Customer 1", 250, "Overdue"],
}
self.check_expected_values(invoices, expected_value)
si = frappe.get_doc("Sales Invoice", invoices[0])
si = frappe.get_doc("Sales Invoice", invoices[0])
# Check if update stock is not enabled
self.assertEqual(si.update_stock, 0)
finally:
property_setter.delete()
clear_doctype_cache("Sales Invoice")
# Check if update stock is not enabled
self.assertEqual(si.update_stock, 0)
def check_expected_values(self, invoices, expected_value, invoice_type="Sales"):
doctype = "Sales Invoice" if invoice_type == "Sales" else "Purchase Invoice"
@@ -75,15 +83,30 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
company = "_Test Opening Invoice Company"
party_1, party_2 = make_customer("Customer A"), make_customer("Customer B")
old_default_receivable_account = frappe.db.get_value("Company", company, "default_receivable_account")
old_default_receivable_account = frappe.db.get_value(
"Company", company, "default_receivable_account"
)
frappe.db.set_value("Company", company, "default_receivable_account", "")
if not frappe.db.exists("Cost Center", "_Test Opening Invoice Company - _TOIC"):
cc = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "_Test Opening Invoice Company",
"is_group": 1, "company": "_Test Opening Invoice Company"})
cc = frappe.get_doc(
{
"doctype": "Cost Center",
"cost_center_name": "_Test Opening Invoice Company",
"is_group": 1,
"company": "_Test Opening Invoice Company",
}
)
cc.insert(ignore_mandatory=True)
cc2 = frappe.get_doc({"doctype": "Cost Center", "cost_center_name": "Main", "is_group": 0,
"company": "_Test Opening Invoice Company", "parent_cost_center": cc.name})
cc2 = frappe.get_doc(
{
"doctype": "Cost Center",
"cost_center_name": "Main",
"is_group": 0,
"company": "_Test Opening Invoice Company",
"parent_cost_center": cc.name,
}
)
cc2.insert()
frappe.db.set_value("Company", company, "cost_center", "Main - _TOIC")
@@ -91,28 +114,37 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
self.make_invoices(company="_Test Opening Invoice Company", party_1=party_1, party_2=party_2)
# Check if missing debit account error raised
error_log = frappe.db.exists("Error Log", {"error": ["like", "%erpnext.controllers.accounts_controller.AccountMissingError%"]})
error_log = frappe.db.exists(
"Error Log",
{"error": ["like", "%erpnext.controllers.accounts_controller.AccountMissingError%"]},
)
self.assertTrue(error_log)
# teardown
frappe.db.set_value("Company", company, "default_receivable_account", old_default_receivable_account)
frappe.db.set_value(
"Company", company, "default_receivable_account", old_default_receivable_account
)
def test_renaming_of_invoice_using_invoice_number_field(self):
company = "_Test Opening Invoice Company"
party_1, party_2 = make_customer("Customer A"), make_customer("Customer B")
self.make_invoices(company=company, party_1=party_1, party_2=party_2, invoice_number="TEST-NEW-INV-11")
self.make_invoices(
company=company, party_1=party_1, party_2=party_2, invoice_number="TEST-NEW-INV-11"
)
sales_inv1 = frappe.get_all('Sales Invoice', filters={'customer':'Customer A'})[0].get("name")
sales_inv2 = frappe.get_all('Sales Invoice', filters={'customer':'Customer B'})[0].get("name")
sales_inv1 = frappe.get_all("Sales Invoice", filters={"customer": "Customer A"})[0].get("name")
sales_inv2 = frappe.get_all("Sales Invoice", filters={"customer": "Customer B"})[0].get("name")
self.assertEqual(sales_inv1, "TEST-NEW-INV-11")
#teardown
# teardown
for inv in [sales_inv1, sales_inv2]:
doc = frappe.get_doc('Sales Invoice', inv)
doc = frappe.get_doc("Sales Invoice", inv)
doc.cancel()
def test_opening_invoice_with_accounting_dimension(self):
invoices = self.make_invoices(invoice_type="Sales", company="_Test Opening Invoice Company", department='Sales - _TOIC')
invoices = self.make_invoices(
invoice_type="Sales", company="_Test Opening Invoice Company", department="Sales - _TOIC"
)
expected_value = {
"keys": ["customer", "outstanding_amount", "status", "department"],
@@ -124,40 +156,44 @@ class TestOpeningInvoiceCreationTool(unittest.TestCase):
def tearDown(self):
disable_dimension()
def get_opening_invoice_creation_dict(**args):
party = "Customer" if args.get("invoice_type", "Sales") == "Sales" else "Supplier"
company = args.get("company", "_Test Company")
invoice_dict = frappe._dict({
"company": company,
"invoice_type": args.get("invoice_type", "Sales"),
"invoices": [
{
"qty": 1.0,
"outstanding_amount": 300,
"party": args.get("party_1") or "_Test {0}".format(party),
"item_name": "Opening Item",
"due_date": "2016-09-10",
"posting_date": "2016-09-05",
"temporary_opening_account": get_temporary_opening_account(company),
"invoice_number": args.get("invoice_number")
},
{
"qty": 2.0,
"outstanding_amount": 250,
"party": args.get("party_2") or "_Test {0} 1".format(party),
"item_name": "Opening Item",
"due_date": "2016-09-10",
"posting_date": "2016-09-05",
"temporary_opening_account": get_temporary_opening_account(company),
"invoice_number": None
}
]
})
invoice_dict = frappe._dict(
{
"company": company,
"invoice_type": args.get("invoice_type", "Sales"),
"invoices": [
{
"qty": 1.0,
"outstanding_amount": 300,
"party": args.get("party_1") or "_Test {0}".format(party),
"item_name": "Opening Item",
"due_date": "2016-09-10",
"posting_date": "2016-09-05",
"temporary_opening_account": get_temporary_opening_account(company),
"invoice_number": args.get("invoice_number"),
},
{
"qty": 2.0,
"outstanding_amount": 250,
"party": args.get("party_2") or "_Test {0} 1".format(party),
"item_name": "Opening Item",
"due_date": "2016-09-10",
"posting_date": "2016-09-05",
"temporary_opening_account": get_temporary_opening_account(company),
"invoice_number": None,
},
],
}
)
invoice_dict.update(args)
return invoice_dict
def make_company():
if frappe.db.exists("Company", "_Test Opening Invoice Company"):
return frappe.get_doc("Company", "_Test Opening Invoice Company")
@@ -170,15 +206,18 @@ def make_company():
company.insert()
return company
def make_customer(customer=None):
customer_name = customer or "Opening Customer"
customer = frappe.get_doc({
"doctype": "Customer",
"customer_name": customer_name,
"customer_group": "All Customer Groups",
"customer_type": "Company",
"territory": "All Territories"
})
customer = frappe.get_doc(
{
"doctype": "Customer",
"customer_name": customer_name,
"customer_group": "All Customer Groups",
"customer_type": "Company",
"territory": "All Territories",
}
)
if not frappe.db.exists("Customer", customer_name):
customer.insert(ignore_permissions=True)
return customer.name

View File

@@ -3,6 +3,7 @@
"creation": "2014-08-29 16:02:39.740505",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"company",
"account"
@@ -11,6 +12,7 @@
{
"fieldname": "company",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"in_list_view": 1,
"label": "Company",
"options": "Company",
@@ -27,7 +29,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-04-07 18:13:08.833822",
"modified": "2022-04-04 12:31:02.994197",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Party Account",
@@ -35,5 +37,6 @@
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC"
"sort_order": "DESC",
"states": []
}

View File

@@ -8,34 +8,43 @@ from frappe.model.document import Document
class PartyLink(Document):
def validate(self):
if self.primary_role not in ['Customer', 'Supplier']:
frappe.throw(_("Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only."),
title=_("Invalid Primary Role"))
if self.primary_role not in ["Customer", "Supplier"]:
frappe.throw(
_(
"Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only."
),
title=_("Invalid Primary Role"),
)
existing_party_link = frappe.get_all('Party Link', {
'primary_party': self.secondary_party
}, pluck="primary_role")
existing_party_link = frappe.get_all(
"Party Link", {"primary_party": self.secondary_party}, pluck="primary_role"
)
if existing_party_link:
frappe.throw(_('{} {} is already linked with another {}')
.format(self.secondary_role, self.secondary_party, existing_party_link[0]))
frappe.throw(
_("{} {} is already linked with another {}").format(
self.secondary_role, self.secondary_party, existing_party_link[0]
)
)
existing_party_link = frappe.get_all('Party Link', {
'secondary_party': self.primary_party
}, pluck="primary_role")
existing_party_link = frappe.get_all(
"Party Link", {"secondary_party": self.primary_party}, pluck="primary_role"
)
if existing_party_link:
frappe.throw(_('{} {} is already linked with another {}')
.format(self.primary_role, self.primary_party, existing_party_link[0]))
frappe.throw(
_("{} {} is already linked with another {}").format(
self.primary_role, self.primary_party, existing_party_link[0]
)
)
@frappe.whitelist()
def create_party_link(primary_role, primary_party, secondary_party):
party_link = frappe.new_doc('Party Link')
party_link = frappe.new_doc("Party Link")
party_link.primary_role = primary_role
party_link.primary_party = primary_party
party_link.secondary_role = 'Customer' if primary_role == 'Supplier' else 'Supplier'
party_link.secondary_role = "Customer" if primary_role == "Supplier" else "Supplier"
party_link.secondary_party = secondary_party
party_link.save(ignore_permissions=True)
return party_link

View File

@@ -196,8 +196,14 @@ frappe.ui.form.on('Payment Entry', {
frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency));
frm.toggle_display("base_paid_amount", frm.doc.paid_from_account_currency != company_currency);
frm.toggle_display("base_total_taxes_and_charges", frm.doc.total_taxes_and_charges &&
(frm.doc.paid_from_account_currency != company_currency));
if (frm.doc.payment_type == "Pay") {
frm.toggle_display("base_total_taxes_and_charges", frm.doc.total_taxes_and_charges &&
(frm.doc.paid_to_account_currency != company_currency));
} else {
frm.toggle_display("base_total_taxes_and_charges", frm.doc.total_taxes_and_charges &&
(frm.doc.paid_from_account_currency != company_currency));
}
frm.toggle_display("base_received_amount", (
frm.doc.paid_to_account_currency != company_currency
@@ -220,10 +226,7 @@ frappe.ui.form.on('Payment Entry', {
(frm.doc.total_allocated_amount > party_amount)));
frm.toggle_display("set_exchange_gain_loss",
(frm.doc.paid_amount && frm.doc.received_amount && frm.doc.difference_amount &&
((frm.doc.paid_from_account_currency != company_currency ||
frm.doc.paid_to_account_currency != company_currency) &&
frm.doc.paid_from_account_currency != frm.doc.paid_to_account_currency)));
frm.doc.paid_amount && frm.doc.received_amount && frm.doc.difference_amount);
frm.refresh_fields();
},
@@ -232,7 +235,8 @@ frappe.ui.form.on('Payment Entry', {
var company_currency = frm.doc.company? frappe.get_doc(":Company", frm.doc.company).default_currency: "";
frm.set_currency_labels(["base_paid_amount", "base_received_amount", "base_total_allocated_amount",
"difference_amount", "base_paid_amount_after_tax", "base_received_amount_after_tax"], company_currency);
"difference_amount", "base_paid_amount_after_tax", "base_received_amount_after_tax",
"base_total_taxes_and_charges"], company_currency);
frm.set_currency_labels(["paid_amount"], frm.doc.paid_from_account_currency);
frm.set_currency_labels(["received_amount"], frm.doc.paid_to_account_currency);
@@ -341,6 +345,8 @@ frappe.ui.form.on('Payment Entry', {
}
frm.set_party_account_based_on_party = true;
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
return frappe.call({
method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_party_details",
args: {
@@ -374,7 +380,11 @@ frappe.ui.form.on('Payment Entry', {
if (r.message.bank_account) {
frm.set_value("bank_account", r.message.bank_account);
}
}
},
() => frm.events.set_current_exchange_rate(frm, "source_exchange_rate",
frm.doc.paid_from_account_currency, company_currency),
() => frm.events.set_current_exchange_rate(frm, "target_exchange_rate",
frm.doc.paid_to_account_currency, company_currency)
]);
}
}
@@ -478,14 +488,14 @@ frappe.ui.form.on('Payment Entry', {
},
paid_from_account_currency: function(frm) {
if(!frm.doc.paid_from_account_currency) return;
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
if(!frm.doc.paid_from_account_currency || !frm.doc.company) return;
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
if (frm.doc.paid_from_account_currency == company_currency) {
frm.set_value("source_exchange_rate", 1);
} else if (frm.doc.paid_from){
if (in_list(["Internal Transfer", "Pay"], frm.doc.payment_type)) {
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
frappe.call({
method: "erpnext.setup.utils.get_exchange_rate",
args: {
@@ -505,8 +515,8 @@ frappe.ui.form.on('Payment Entry', {
},
paid_to_account_currency: function(frm) {
if(!frm.doc.paid_to_account_currency) return;
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
if(!frm.doc.paid_to_account_currency || !frm.doc.company) return;
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
frm.events.set_current_exchange_rate(frm, "target_exchange_rate",
frm.doc.paid_to_account_currency, company_currency);

View File

@@ -66,7 +66,9 @@
"tax_withholding_category",
"section_break_56",
"taxes",
"section_break_60",
"base_total_taxes_and_charges",
"column_break_61",
"total_taxes_and_charges",
"deductions_or_loss_section",
"deductions",
@@ -715,12 +717,21 @@
"fieldtype": "Data",
"hidden": 1,
"label": "Paid To Account Type"
},
{
"fieldname": "column_break_61",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_60",
"fieldtype": "Section Break",
"hide_border": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2021-11-24 18:58:24.919764",
"modified": "2022-02-23 20:08:39.559814",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",
@@ -763,6 +774,7 @@
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "title",
"track_changes": 1
}

File diff suppressed because it is too large Load Diff

View File

@@ -32,10 +32,9 @@ class TestPaymentEntry(unittest.TestCase):
pe.insert()
pe.submit()
expected_gle = dict((d[0], d) for d in [
["Debtors - _TC", 0, 1000, so.name],
["_Test Cash - _TC", 1000.0, 0, None]
])
expected_gle = dict(
(d[0], d) for d in [["Debtors - _TC", 0, 1000, so.name], ["_Test Cash - _TC", 1000.0, 0, None]]
)
self.validate_gl_entries(pe.name, expected_gle)
@@ -48,9 +47,9 @@ class TestPaymentEntry(unittest.TestCase):
self.assertEqual(so_advance_paid, 0)
def test_payment_entry_for_blocked_supplier_invoice(self):
supplier = frappe.get_doc('Supplier', '_Test Supplier')
supplier = frappe.get_doc("Supplier", "_Test Supplier")
supplier.on_hold = 1
supplier.hold_type = 'Invoices'
supplier.hold_type = "Invoices"
supplier.save()
self.assertRaises(frappe.ValidationError, make_purchase_invoice)
@@ -59,32 +58,40 @@ class TestPaymentEntry(unittest.TestCase):
supplier.save()
def test_payment_entry_for_blocked_supplier_payments(self):
supplier = frappe.get_doc('Supplier', '_Test Supplier')
supplier = frappe.get_doc("Supplier", "_Test Supplier")
supplier.on_hold = 1
supplier.hold_type = 'Payments'
supplier.hold_type = "Payments"
supplier.save()
pi = make_purchase_invoice()
self.assertRaises(
frappe.ValidationError, get_payment_entry, dt='Purchase Invoice', dn=pi.name,
bank_account="_Test Bank - _TC")
frappe.ValidationError,
get_payment_entry,
dt="Purchase Invoice",
dn=pi.name,
bank_account="_Test Bank - _TC",
)
supplier.on_hold = 0
supplier.save()
def test_payment_entry_for_blocked_supplier_payments_today_date(self):
supplier = frappe.get_doc('Supplier', '_Test Supplier')
supplier = frappe.get_doc("Supplier", "_Test Supplier")
supplier.on_hold = 1
supplier.hold_type = 'Payments'
supplier.hold_type = "Payments"
supplier.release_date = nowdate()
supplier.save()
pi = make_purchase_invoice()
self.assertRaises(
frappe.ValidationError, get_payment_entry, dt='Purchase Invoice', dn=pi.name,
bank_account="_Test Bank - _TC")
frappe.ValidationError,
get_payment_entry,
dt="Purchase Invoice",
dn=pi.name,
bank_account="_Test Bank - _TC",
)
supplier.on_hold = 0
supplier.save()
@@ -93,15 +100,15 @@ class TestPaymentEntry(unittest.TestCase):
# this test is meant to fail only if something fails in the try block
with self.assertRaises(Exception):
try:
supplier = frappe.get_doc('Supplier', '_Test Supplier')
supplier = frappe.get_doc("Supplier", "_Test Supplier")
supplier.on_hold = 1
supplier.hold_type = 'Payments'
supplier.release_date = '2018-03-01'
supplier.hold_type = "Payments"
supplier.release_date = "2018-03-01"
supplier.save()
pi = make_purchase_invoice()
get_payment_entry('Purchase Invoice', pi.name, bank_account="_Test Bank - _TC")
get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
supplier.on_hold = 0
supplier.save()
@@ -111,8 +118,12 @@ class TestPaymentEntry(unittest.TestCase):
raise Exception
def test_payment_entry_against_si_usd_to_usd(self):
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
currency="USD", conversion_rate=50)
si = create_sales_invoice(
customer="_Test Customer USD",
debit_to="_Test Receivable USD - _TC",
currency="USD",
conversion_rate=50,
)
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
@@ -120,10 +131,13 @@ class TestPaymentEntry(unittest.TestCase):
pe.insert()
pe.submit()
expected_gle = dict((d[0], d) for d in [
["_Test Receivable USD - _TC", 0, 5000, si.name],
["_Test Bank USD - _TC", 5000.0, 0, None]
])
expected_gle = dict(
(d[0], d)
for d in [
["_Test Receivable USD - _TC", 0, 5000, si.name],
["_Test Bank USD - _TC", 5000.0, 0, None],
]
)
self.validate_gl_entries(pe.name, expected_gle)
@@ -136,8 +150,12 @@ class TestPaymentEntry(unittest.TestCase):
self.assertEqual(outstanding_amount, 100)
def test_payment_entry_against_pi(self):
pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
currency="USD", conversion_rate=50)
pi = make_purchase_invoice(
supplier="_Test Supplier USD",
debit_to="_Test Payable USD - _TC",
currency="USD",
conversion_rate=50,
)
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC")
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
@@ -145,20 +163,26 @@ class TestPaymentEntry(unittest.TestCase):
pe.insert()
pe.submit()
expected_gle = dict((d[0], d) for d in [
["_Test Payable USD - _TC", 12500, 0, pi.name],
["_Test Bank USD - _TC", 0, 12500, None]
])
expected_gle = dict(
(d[0], d)
for d in [
["_Test Payable USD - _TC", 12500, 0, pi.name],
["_Test Bank USD - _TC", 0, 12500, None],
]
)
self.validate_gl_entries(pe.name, expected_gle)
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", pi.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 0)
def test_payment_against_sales_invoice_to_check_status(self):
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
currency="USD", conversion_rate=50)
si = create_sales_invoice(
customer="_Test Customer USD",
debit_to="_Test Receivable USD - _TC",
currency="USD",
conversion_rate=50,
)
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
pe.reference_no = "1"
@@ -167,28 +191,35 @@ class TestPaymentEntry(unittest.TestCase):
pe.insert()
pe.submit()
outstanding_amount, status = frappe.db.get_value("Sales Invoice", si.name, ["outstanding_amount", "status"])
outstanding_amount, status = frappe.db.get_value(
"Sales Invoice", si.name, ["outstanding_amount", "status"]
)
self.assertEqual(flt(outstanding_amount), 0)
self.assertEqual(status, 'Paid')
self.assertEqual(status, "Paid")
pe.cancel()
outstanding_amount, status = frappe.db.get_value("Sales Invoice", si.name, ["outstanding_amount", "status"])
outstanding_amount, status = frappe.db.get_value(
"Sales Invoice", si.name, ["outstanding_amount", "status"]
)
self.assertEqual(flt(outstanding_amount), 100)
self.assertEqual(status, 'Unpaid')
self.assertEqual(status, "Unpaid")
def test_payment_entry_against_payment_terms(self):
si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
create_payment_terms_template()
si.payment_terms_template = 'Test Receivable Template'
si.payment_terms_template = "Test Receivable Template"
si.append('taxes', {
"charge_type": "On Net Total",
"account_head": "_Test Account Service Tax - _TC",
"cost_center": "_Test Cost Center - _TC",
"description": "Service Tax",
"rate": 18
})
si.append(
"taxes",
{
"charge_type": "On Net Total",
"account_head": "_Test Account Service Tax - _TC",
"cost_center": "_Test Cost Center - _TC",
"description": "Service Tax",
"rate": 18,
},
)
si.save()
si.submit()
@@ -197,25 +228,28 @@ class TestPaymentEntry(unittest.TestCase):
pe.submit()
si.load_from_db()
self.assertEqual(pe.references[0].payment_term, 'Basic Amount Receivable')
self.assertEqual(pe.references[1].payment_term, 'Tax Receivable')
self.assertEqual(pe.references[0].payment_term, "Basic Amount Receivable")
self.assertEqual(pe.references[1].payment_term, "Tax Receivable")
self.assertEqual(si.payment_schedule[0].paid_amount, 200.0)
self.assertEqual(si.payment_schedule[1].paid_amount, 36.0)
def test_payment_entry_against_payment_terms_with_discount(self):
si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
create_payment_terms_template_with_discount()
si.payment_terms_template = 'Test Discount Template'
si.payment_terms_template = "Test Discount Template"
frappe.db.set_value('Company', si.company, 'default_discount_account', 'Write Off - _TC')
frappe.db.set_value("Company", si.company, "default_discount_account", "Write Off - _TC")
si.append('taxes', {
"charge_type": "On Net Total",
"account_head": "_Test Account Service Tax - _TC",
"cost_center": "_Test Cost Center - _TC",
"description": "Service Tax",
"rate": 18
})
si.append(
"taxes",
{
"charge_type": "On Net Total",
"account_head": "_Test Account Service Tax - _TC",
"cost_center": "_Test Cost Center - _TC",
"description": "Service Tax",
"rate": 18,
},
)
si.save()
si.submit()
@@ -224,16 +258,19 @@ class TestPaymentEntry(unittest.TestCase):
pe.submit()
si.load_from_db()
self.assertEqual(pe.references[0].payment_term, '30 Credit Days with 10% Discount')
self.assertEqual(pe.references[0].payment_term, "30 Credit Days with 10% Discount")
self.assertEqual(si.payment_schedule[0].payment_amount, 236.0)
self.assertEqual(si.payment_schedule[0].paid_amount, 212.40)
self.assertEqual(si.payment_schedule[0].outstanding, 0)
self.assertEqual(si.payment_schedule[0].discounted_amount, 23.6)
def test_payment_against_purchase_invoice_to_check_status(self):
pi = make_purchase_invoice(supplier="_Test Supplier USD", debit_to="_Test Payable USD - _TC",
currency="USD", conversion_rate=50)
pi = make_purchase_invoice(
supplier="_Test Supplier USD",
debit_to="_Test Payable USD - _TC",
currency="USD",
conversion_rate=50,
)
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC")
pe.reference_no = "1"
@@ -242,21 +279,27 @@ class TestPaymentEntry(unittest.TestCase):
pe.insert()
pe.submit()
outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"])
outstanding_amount, status = frappe.db.get_value(
"Purchase Invoice", pi.name, ["outstanding_amount", "status"]
)
self.assertEqual(flt(outstanding_amount), 0)
self.assertEqual(status, 'Paid')
self.assertEqual(status, "Paid")
pe.cancel()
outstanding_amount, status = frappe.db.get_value("Purchase Invoice", pi.name, ["outstanding_amount", "status"])
outstanding_amount, status = frappe.db.get_value(
"Purchase Invoice", pi.name, ["outstanding_amount", "status"]
)
self.assertEqual(flt(outstanding_amount), 250)
self.assertEqual(status, 'Unpaid')
self.assertEqual(status, "Unpaid")
def test_payment_entry_against_ec(self):
payable = frappe.get_cached_value('Company', "_Test Company", 'default_payable_account')
payable = frappe.get_cached_value("Company", "_Test Company", "default_payable_account")
ec = make_expense_claim(payable, 300, 300, "_Test Company", "Travel Expenses - _TC")
pe = get_payment_entry("Expense Claim", ec.name, bank_account="_Test Bank USD - _TC", bank_amount=300)
pe = get_payment_entry(
"Expense Claim", ec.name, bank_account="_Test Bank USD - _TC", bank_amount=300
)
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
pe.source_exchange_rate = 1
@@ -264,68 +307,87 @@ class TestPaymentEntry(unittest.TestCase):
pe.insert()
pe.submit()
expected_gle = dict((d[0], d) for d in [
[payable, 300, 0, ec.name],
["_Test Bank USD - _TC", 0, 300, None]
])
expected_gle = dict(
(d[0], d) for d in [[payable, 300, 0, ec.name], ["_Test Bank USD - _TC", 0, 300, None]]
)
self.validate_gl_entries(pe.name, expected_gle)
outstanding_amount = flt(frappe.db.get_value("Expense Claim", ec.name, "total_sanctioned_amount")) - \
flt(frappe.db.get_value("Expense Claim", ec.name, "total_amount_reimbursed"))
outstanding_amount = flt(
frappe.db.get_value("Expense Claim", ec.name, "total_sanctioned_amount")
) - flt(frappe.db.get_value("Expense Claim", ec.name, "total_amount_reimbursed"))
self.assertEqual(outstanding_amount, 0)
def test_payment_entry_against_si_usd_to_inr(self):
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
currency="USD", conversion_rate=50)
pe = get_payment_entry("Sales Invoice", si.name, party_amount=20,
bank_account="_Test Bank - _TC", bank_amount=900)
si = create_sales_invoice(
customer="_Test Customer USD",
debit_to="_Test Receivable USD - _TC",
currency="USD",
conversion_rate=50,
)
pe = get_payment_entry(
"Sales Invoice", si.name, party_amount=20, bank_account="_Test Bank - _TC", bank_amount=900
)
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
self.assertEqual(pe.difference_amount, 100)
pe.append("deductions", {
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": 100
})
pe.append(
"deductions",
{
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": 100,
},
)
pe.insert()
pe.submit()
expected_gle = dict((d[0], d) for d in [
["_Test Receivable USD - _TC", 0, 1000, si.name],
["_Test Bank - _TC", 900, 0, None],
["_Test Exchange Gain/Loss - _TC", 100.0, 0, None],
])
expected_gle = dict(
(d[0], d)
for d in [
["_Test Receivable USD - _TC", 0, 1000, si.name],
["_Test Bank - _TC", 900, 0, None],
["_Test Exchange Gain/Loss - _TC", 100.0, 0, None],
]
)
self.validate_gl_entries(pe.name, expected_gle)
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 80)
def test_payment_entry_against_si_usd_to_usd_with_deduction_in_base_currency (self):
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
currency="USD", conversion_rate=50, do_not_save=1)
def test_payment_entry_against_si_usd_to_usd_with_deduction_in_base_currency(self):
si = create_sales_invoice(
customer="_Test Customer USD",
debit_to="_Test Receivable USD - _TC",
currency="USD",
conversion_rate=50,
do_not_save=1,
)
si.plc_conversion_rate = 50
si.save()
si.submit()
pe = get_payment_entry("Sales Invoice", si.name, party_amount=20,
bank_account="_Test Bank USD - _TC", bank_amount=900)
pe = get_payment_entry(
"Sales Invoice", si.name, party_amount=20, bank_account="_Test Bank USD - _TC", bank_amount=900
)
pe.source_exchange_rate = 45.263
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.append(
"deductions",
{
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": 94.80,
},
)
pe.save()
@@ -359,8 +421,7 @@ class TestPaymentEntry(unittest.TestCase):
pe.set_amounts()
self.assertEqual(
pe.source_exchange_rate, 65.1,
"{0} is not equal to {1}".format(pe.source_exchange_rate, 65.1)
pe.source_exchange_rate, 65.1, "{0} is not equal to {1}".format(pe.source_exchange_rate, 65.1)
)
def test_internal_transfer_usd_to_inr(self):
@@ -382,20 +443,26 @@ class TestPaymentEntry(unittest.TestCase):
self.assertEqual(pe.difference_amount, 500)
pe.append("deductions", {
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": 500
})
pe.append(
"deductions",
{
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": 500,
},
)
pe.insert()
pe.submit()
expected_gle = dict((d[0], d) for d in [
["_Test Bank USD - _TC", 0, 5000, None],
["_Test Bank - _TC", 4500, 0, None],
["_Test Exchange Gain/Loss - _TC", 500.0, 0, None],
])
expected_gle = dict(
(d[0], d)
for d in [
["_Test Bank USD - _TC", 0, 5000, None],
["_Test Bank - _TC", 4500, 0, None],
["_Test Exchange Gain/Loss - _TC", 500.0, 0, None],
]
)
self.validate_gl_entries(pe.name, expected_gle)
@@ -435,10 +502,9 @@ class TestPaymentEntry(unittest.TestCase):
pe3.insert()
pe3.submit()
expected_gle = dict((d[0], d) for d in [
["Debtors - _TC", 100, 0, si1.name],
["_Test Cash - _TC", 0, 100, None]
])
expected_gle = dict(
(d[0], d) for d in [["Debtors - _TC", 100, 0, si1.name], ["_Test Cash - _TC", 0, 100, None]]
)
self.validate_gl_entries(pe3.name, expected_gle)
@@ -462,12 +528,16 @@ class TestPaymentEntry(unittest.TestCase):
self.assertEqual(expected_gle[gle.account][3], gle.against_voucher)
def get_gle(self, voucher_no):
return frappe.db.sql("""select account, debit, credit, against_voucher
return frappe.db.sql(
"""select account, debit, credit, against_voucher
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
order by account asc""", voucher_no, as_dict=1)
order by account asc""",
voucher_no,
as_dict=1,
)
def test_payment_entry_write_off_difference(self):
si = create_sales_invoice()
si = create_sales_invoice()
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC")
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
@@ -477,11 +547,10 @@ class TestPaymentEntry(unittest.TestCase):
self.assertEqual(pe.unallocated_amount, 10)
pe.received_amount = pe.paid_amount = 95
pe.append("deductions", {
"account": "_Test Write Off - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": 5
})
pe.append(
"deductions",
{"account": "_Test Write Off - _TC", "cost_center": "_Test Cost Center - _TC", "amount": 5},
)
pe.save()
self.assertEqual(pe.unallocated_amount, 0)
@@ -489,27 +558,37 @@ class TestPaymentEntry(unittest.TestCase):
pe.submit()
expected_gle = dict((d[0], d) for d in [
["Debtors - _TC", 0, 100, si.name],
["_Test Cash - _TC", 95, 0, None],
["_Test Write Off - _TC", 5, 0, None]
])
expected_gle = dict(
(d[0], d)
for d in [
["Debtors - _TC", 0, 100, si.name],
["_Test Cash - _TC", 95, 0, None],
["_Test Write Off - _TC", 5, 0, None],
]
)
self.validate_gl_entries(pe.name, expected_gle)
def test_payment_entry_exchange_gain_loss(self):
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
currency="USD", conversion_rate=50)
si = create_sales_invoice(
customer="_Test Customer USD",
debit_to="_Test Receivable USD - _TC",
currency="USD",
conversion_rate=50,
)
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
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.append(
"deductions",
{
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": -500,
},
)
pe.save()
self.assertEqual(pe.unallocated_amount, 0)
@@ -517,11 +596,14 @@ class TestPaymentEntry(unittest.TestCase):
pe.submit()
expected_gle = dict((d[0], d) for d in [
["_Test Receivable USD - _TC", 0, 5000, si.name],
["_Test Bank USD - _TC", 5500, 0, None],
["_Test Exchange Gain/Loss - _TC", 0, 500, None],
])
expected_gle = dict(
(d[0], d)
for d in [
["_Test Receivable USD - _TC", 0, 5000, 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)
@@ -530,10 +612,11 @@ class TestPaymentEntry(unittest.TestCase):
def test_payment_entry_against_sales_invoice_with_cost_centre(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
self.assertEqual(pe.cost_center, si.cost_center)
@@ -546,18 +629,18 @@ class TestPaymentEntry(unittest.TestCase):
pe.submit()
expected_values = {
"_Test Bank - _TC": {
"cost_center": cost_center
},
"Debtors - _TC": {
"cost_center": cost_center
}
"_Test Bank - _TC": {"cost_center": cost_center},
"Debtors - _TC": {"cost_center": cost_center},
}
gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit,
gl_entries = frappe.db.sql(
"""select account, cost_center, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
order by account asc""", pe.name, as_dict=1)
order by account asc""",
pe.name,
as_dict=1,
)
self.assertTrue(gl_entries)
@@ -566,10 +649,13 @@ class TestPaymentEntry(unittest.TestCase):
def test_payment_entry_against_purchase_invoice_with_cost_center(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
pi = make_purchase_invoice_against_cost_center(cost_center=cost_center, credit_to="Creditors - _TC")
pi = make_purchase_invoice_against_cost_center(
cost_center=cost_center, credit_to="Creditors - _TC"
)
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
self.assertEqual(pe.cost_center, pi.cost_center)
@@ -582,18 +668,18 @@ class TestPaymentEntry(unittest.TestCase):
pe.submit()
expected_values = {
"_Test Bank - _TC": {
"cost_center": cost_center
},
"Creditors - _TC": {
"cost_center": cost_center
}
"_Test Bank - _TC": {"cost_center": cost_center},
"Creditors - _TC": {"cost_center": cost_center},
}
gl_entries = frappe.db.sql("""select account, cost_center, account_currency, debit, credit,
gl_entries = frappe.db.sql(
"""select account, cost_center, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Payment Entry' and voucher_no=%s
order by account asc""", pe.name, as_dict=1)
order by account asc""",
pe.name,
as_dict=1,
)
self.assertTrue(gl_entries)
@@ -603,13 +689,16 @@ class TestPaymentEntry(unittest.TestCase):
def test_payment_entry_account_and_party_balance_with_cost_center(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.utils import get_balance_on
cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=si.cost_center)
party_balance = get_balance_on(party_type="Customer", party=si.customer, cost_center=si.cost_center)
party_balance = get_balance_on(
party_type="Customer", party=si.customer, cost_center=si.cost_center
)
party_account_balance = get_balance_on(si.debit_to, cost_center=si.cost_center)
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
@@ -633,56 +722,125 @@ class TestPaymentEntry(unittest.TestCase):
self.assertEqual(flt(expected_party_balance), party_balance)
self.assertEqual(flt(expected_party_account_balance), party_account_balance)
def test_multi_currency_payment_entry_with_taxes(self):
payment_entry = create_payment_entry(
party="_Test Supplier USD", paid_to="_Test Payable USD - _TC", save=True
)
payment_entry.append(
"taxes",
{
"account_head": "_Test Account Service Tax - _TC",
"charge_type": "Actual",
"tax_amount": 10,
"add_deduct_tax": "Add",
"description": "Test",
},
)
payment_entry.save()
self.assertEqual(payment_entry.base_total_taxes_and_charges, 10)
self.assertEqual(
flt(payment_entry.total_taxes_and_charges, 2), flt(10 / payment_entry.target_exchange_rate, 2)
)
def test_payment_entry_against_onhold_purchase_invoice(self):
pi = make_purchase_invoice()
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank USD - _TC")
pe.reference_no = "1"
pe.reference_date = "2016-01-01"
# block invoice after creating payment entry
# since `get_payment_entry` will not attach blocked invoice to payment
pi.block_invoice()
with self.assertRaises(frappe.ValidationError) as err:
pe.save()
self.assertTrue("is on hold" in str(err.exception).lower())
def create_payment_entry(**args):
payment_entry = frappe.new_doc("Payment Entry")
payment_entry.company = args.get("company") or "_Test Company"
payment_entry.payment_type = args.get("payment_type") or "Pay"
payment_entry.party_type = args.get("party_type") or "Supplier"
payment_entry.party = args.get("party") or "_Test Supplier"
payment_entry.paid_from = args.get("paid_from") or "_Test Bank - _TC"
payment_entry.paid_to = args.get("paid_to") or "Creditors - _TC"
payment_entry.paid_amount = args.get("paid_amount") or 1000
payment_entry.setup_party_account_field()
payment_entry.set_missing_values()
payment_entry.set_exchange_rate()
payment_entry.received_amount = payment_entry.paid_amount / payment_entry.target_exchange_rate
payment_entry.reference_no = "Test001"
payment_entry.reference_date = nowdate()
if args.get("save"):
payment_entry.save()
if args.get("submit"):
payment_entry.submit()
return payment_entry
def create_payment_terms_template():
create_payment_term('Basic Amount Receivable')
create_payment_term('Tax Receivable')
create_payment_term("Basic Amount Receivable")
create_payment_term("Tax Receivable")
if not frappe.db.exists('Payment Terms Template', 'Test Receivable Template'):
payment_term_template = frappe.get_doc({
'doctype': 'Payment Terms Template',
'template_name': 'Test Receivable Template',
'allocate_payment_based_on_payment_terms': 1,
'terms': [{
'doctype': 'Payment Terms Template Detail',
'payment_term': 'Basic Amount Receivable',
'invoice_portion': 84.746,
'credit_days_based_on': 'Day(s) after invoice date',
'credit_days': 1
},
if not frappe.db.exists("Payment Terms Template", "Test Receivable Template"):
payment_term_template = frappe.get_doc(
{
'doctype': 'Payment Terms Template Detail',
'payment_term': 'Tax Receivable',
'invoice_portion': 15.254,
'credit_days_based_on': 'Day(s) after invoice date',
'credit_days': 2
}]
}).insert()
"doctype": "Payment Terms Template",
"template_name": "Test Receivable Template",
"allocate_payment_based_on_payment_terms": 1,
"terms": [
{
"doctype": "Payment Terms Template Detail",
"payment_term": "Basic Amount Receivable",
"invoice_portion": 84.746,
"credit_days_based_on": "Day(s) after invoice date",
"credit_days": 1,
},
{
"doctype": "Payment Terms Template Detail",
"payment_term": "Tax Receivable",
"invoice_portion": 15.254,
"credit_days_based_on": "Day(s) after invoice date",
"credit_days": 2,
},
],
}
).insert()
def create_payment_terms_template_with_discount():
create_payment_term('30 Credit Days with 10% Discount')
create_payment_term("30 Credit Days with 10% Discount")
if not frappe.db.exists("Payment Terms Template", "Test Discount Template"):
payment_term_template = frappe.get_doc(
{
"doctype": "Payment Terms Template",
"template_name": "Test Discount Template",
"allocate_payment_based_on_payment_terms": 1,
"terms": [
{
"doctype": "Payment Terms Template Detail",
"payment_term": "30 Credit Days with 10% Discount",
"invoice_portion": 100,
"credit_days_based_on": "Day(s) after invoice date",
"credit_days": 2,
"discount": 10,
"discount_validity_based_on": "Day(s) after invoice date",
"discount_validity": 1,
}
],
}
).insert()
if not frappe.db.exists('Payment Terms Template', 'Test Discount Template'):
payment_term_template = frappe.get_doc({
'doctype': 'Payment Terms Template',
'template_name': 'Test Discount Template',
'allocate_payment_based_on_payment_terms': 1,
'terms': [{
'doctype': 'Payment Terms Template Detail',
'payment_term': '30 Credit Days with 10% Discount',
'invoice_portion': 100,
'credit_days_based_on': 'Day(s) after invoice date',
'credit_days': 2,
'discount': 10,
'discount_validity_based_on': 'Day(s) after invoice date',
'discount_validity': 1
}]
}).insert()
def create_payment_term(name):
if not frappe.db.exists('Payment Term', name):
frappe.get_doc({
'doctype': 'Payment Term',
'payment_term_name': name
}).insert()
if not frappe.db.exists("Payment Term", name):
frappe.get_doc({"doctype": "Payment Term", "payment_term_name": name}).insert()

View File

@@ -18,10 +18,13 @@ class PaymentGatewayAccount(Document):
def update_default_payment_gateway(self):
if self.is_default:
frappe.db.sql("""update `tabPayment Gateway Account` set is_default = 0
where is_default = 1 """)
frappe.db.sql(
"""update `tabPayment Gateway Account` set is_default = 0
where is_default = 1 """
)
def set_as_default_if_not_set(self):
if not frappe.db.get_value("Payment Gateway Account",
{"is_default": 1, "name": ("!=", self.name)}, "name"):
if not frappe.db.get_value(
"Payment Gateway Account", {"is_default": 1, "name": ("!=", self.name)}, "name"
):
self.is_default = 1

View File

@@ -1,17 +1,6 @@
def get_data():
return {
'fieldname': 'payment_gateway_account',
'non_standard_fieldnames': {
'Subscription Plan': 'payment_gateway'
},
'transactions': [
{
'items': ['Payment Request']
},
{
'items': ['Subscription Plan']
}
]
"fieldname": "payment_gateway_account",
"non_standard_fieldnames": {"Subscription Plan": "payment_gateway"},
"transactions": [{"items": ["Payment Request"]}, {"items": ["Subscription Plan"]}],
}

View File

@@ -5,5 +5,6 @@ import unittest
# test_records = frappe.get_test_records('Payment Gateway Account')
class TestPaymentGatewayAccount(unittest.TestCase):
pass

View File

@@ -12,7 +12,6 @@ frappe.ui.form.on('Payment Order', {
});
frm.set_df_property('references', 'cannot_add_rows', true);
frm.set_df_property('references', 'cannot_delete_rows', true);
},
refresh: function(frm) {
if (frm.doc.docstatus == 0) {

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