Compare commits

...

1452 Commits

Author SHA1 Message Date
Maharshi Patel
a16347f325 fix: add Document Date in E-Invoice print format 2022-11-08 16:35:34 +05:30
Deepesh Garg
19b9875ba8 Merge pull request #32781 from frappe/mergify/bp/version-13-hotfix/pr-32777
fix: Reset advance paid amount on Order cancel and amend (backport #32777)
2022-11-08 10:53:02 +05:30
Deepesh Garg
43aa670e90 Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into mergify/bp/version-13-hotfix/pr-32777 2022-11-08 10:40:07 +05:30
Deepesh Garg
4f76ed1c68 chore: Resolve conflicts 2022-11-08 10:39:21 +05:30
Deepesh Garg
b93d13feef Merge pull request #32865 from frappe/mergify/bp/version-13-hotfix/pr-32846
fix: add german translations (backport #32846)
2022-11-07 18:42:38 +05:30
Raffael Meyer
e1a32cc620 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-32846 2022-11-07 13:18:06 +01:00
barredterra
550f5f280c chore: resolve merge conflicts 2022-11-07 13:15:38 +01:00
Deepesh Garg
fbbfeb8563 Merge pull request #32873 from frappe/mergify/bp/version-13-hotfix/pr-32802
fix: `Material Consumption` option in case of `Skip Transfer to WIP` in WO (backport #32802)
2022-11-07 10:18:14 +05:30
Sagar Sharma
418c131331 fix: Material Consumption option in case of Skip Transfer to WIP in WO
(cherry picked from commit 8ea6983734)
2022-11-07 04:06:55 +00:00
Raffael Meyer
a8bf17e560 chore: add german translations (#32846)
Mostly for balance sheet

(cherry picked from commit d2b6490bca)

# Conflicts:
#	erpnext/translations/de.csv
2022-11-06 04:53:43 +00:00
Deepesh Garg
9961037f71 Merge pull request #32860 from frappe/mergify/bp/version-13-hotfix/pr-32794
fix: Disable tax included prices for internal transfers (backport #32794)
2022-11-05 21:17:09 +05:30
Deepesh Garg
84ee1b86af fix: Disable tax included prices for internal transfers (#32794)
* fix: Disable tax-included prices for internal transfers

(cherry picked from commit 8d30ebb12b)
2022-11-05 15:22:30 +00:00
Deepesh Garg
afe86d83aa Merge pull request #32857 from frappe/mergify/bp/version-13-hotfix/pr-32847
fix: Create POS Opening Entry POS Profile filter. (backport #32847)
2022-11-05 20:52:12 +05:30
Maharshi Patel
76e4bb44f1 fix: Create POS Opening Entry POS Profile filter.
pos_profile_query was variable instead of function.

(cherry picked from commit 1328a45f2a)
2022-11-05 11:15:58 +00:00
Deepesh Garg
c1bc1040b8 Merge pull request #32834 from frappe/mergify/bp/version-13-hotfix/pr-32773
fix: for asset's purchase_date, if bill_date is set, use that instead of posting_date (backport #32773)
2022-11-03 12:06:50 +05:30
anandbaburajan
1d23c9a9fd fix: for asset's purchase_date, if bill_date is set, use that instead of posting_date
(cherry picked from commit f322c608cf)
2022-11-03 06:28:37 +00:00
Sagar Sharma
5a211813d3 Merge pull request #32821 from frappe/mergify/bp/version-13-hotfix/pr-32788
fix: use `flt` instead of `cint` in `get_batch_no` (backport #32788)
2022-11-02 17:08:08 +05:30
Sagar Sharma
601b1e3821 fix: use flt instead of cint in get_batch_no
(cherry picked from commit 9fb3fb4c83)
2022-11-02 10:25:54 +00:00
Deepesh Garg
a91483899b Merge pull request #32806 from frappe/mergify/bp/version-13-hotfix/pr-32779
fix: Mode of payment for returns in POS Sales Invoice (backport #32779)
2022-11-01 22:11:50 +05:30
Deepesh Garg
2f6d55d8d2 Merge pull request #32803 from frappe/mergify/bp/version-13-hotfix/pr-32801
fix: Issues while cancel/amending Purchase Invoice with TDS enabled (backport #32801)
2022-11-01 22:11:26 +05:30
Deepesh Garg
4a10454d89 chore: Resolve conflicts 2022-11-01 21:30:36 +05:30
Deepesh Garg
12b15347bc chore: Update tests
(cherry picked from commit 5b74161195)
2022-11-01 15:59:54 +00:00
Deepesh Garg
9b63a1a2e9 fix: Mode of payment for returns in POS Sales Invoice
(cherry picked from commit 06e8e28531)
2022-11-01 15:59:53 +00:00
Deepesh Garg
8888957952 fix: Issues while cancel/amending Purchase Invoice with TDS enabled
(cherry picked from commit f7c9258770)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
2022-11-01 15:18:48 +00:00
ruthra kumar
a9660bf026 Merge pull request #32798 from ruthra-kumar/better_approach_to_list_item_removal
fix: filter return pos in reconciliation tool
2022-11-01 17:28:01 +05:30
ruthra kumar
b877b0d3a2 fix: filter return pos in reconciliation tool 2022-11-01 16:56:35 +05:30
Deepesh Garg
e4082093b8 Merge pull request #32796 from deepeshgarg007/itc_eligibility_reverse_charge
fix: Do not force eligibilgity of itc for reverse charge
2022-11-01 16:32:39 +05:30
Deepesh Garg
9dc0edfb8a fix: Do not force eligibilgity of itc for reverse charge 2022-11-01 16:03:32 +05:30
rohitwaghchaure
e13f05ce19 Merge pull request #32784 from rohitwaghchaure/fixed-warehouse-group-for-batch
fix: group warehouse filter not working for Batch-wise Balance history report
2022-10-31 23:34:00 +05:30
Rohit Waghchaure
310e7c522c fix: group warehouse filter not working for Batch-wise Balance history report 2022-10-31 23:21:29 +05:30
Deepesh Garg
e32e0bc8fa fix: Reset advance paid amount on Oreder cancel and amend
(cherry picked from commit 92f37ca111)

# Conflicts:
#	erpnext/buying/doctype/purchase_order/purchase_order.js
2022-10-31 16:09:48 +00:00
Deepesh Garg
0c24ba0118 Merge pull request #32690 from frappe/mergify/bp/version-13-hotfix/pr-32424
feat: Repayment schedule types for term loans (backport #32424)
2022-10-31 11:57:57 +05:30
Deepesh Garg
60fa87751a chore: Update payroll loan tests 2022-10-31 11:28:32 +05:30
Deepesh Garg
50bc7785f7 chore: reload doctypes 2022-10-31 10:39:03 +05:30
Deepesh Garg
d2aea0cd2e chore: fix datetime value 2022-10-30 11:01:41 +05:30
Deepesh Garg
e9006582fb chore: Add removed field 2022-10-30 10:48:37 +05:30
Deepesh Garg
3483e0e2a5 Merge branch 'version-13-hotfix' of https://github.com/frappe/erpnext into mergify/bp/version-13-hotfix/pr-32424 2022-10-30 10:46:21 +05:30
Deepesh Garg
65b047c94c chore: Resync Loan Doc 2022-10-30 10:44:15 +05:30
Sagar Sharma
ada3ce21c0 Merge pull request #32757 from frappe/mergify/bp/version-13-hotfix/pr-32754
fix: add `Sales Order` reference in Material Request Dashboard (backport #32754)
2022-10-29 13:52:28 +05:30
Sagar Sharma
cb89dba5ab fix: add Sales Order reference in Material Request Dashboard
(cherry picked from commit 15ebf4a0cf)
2022-10-29 07:07:45 +00:00
Deepesh Garg
d73237e896 chore: Resolve conflicts 2022-10-29 11:06:53 +05:30
Sagar Sharma
8e0d393cd4 Merge pull request #32741 from frappe/mergify/bp/version-13-hotfix/pr-32738
fix: Added Material Request Reference in Purchase Recipt Dashboard for Tracking (backport #32738)
2022-10-28 16:06:35 +05:30
Vishal
292ff1bd6b chore: minor linting issue fixed
(cherry picked from commit e8c0157017)
2022-10-28 08:55:07 +00:00
Vishal
804cf40f69 chore: Added Material Request Reference in Purchase Recipt Dashboard for Tracking
(cherry picked from commit a04c44fe34)
2022-10-28 08:55:06 +00:00
Deepesh Garg
85f48dd6bd chore: Update tests
(cherry picked from commit e59b147a62)
2022-10-23 14:09:20 +00:00
Deepesh Garg
fa0a137a38 chore: Add repayment date on option
(cherry picked from commit ef0cb17faf)
2022-10-23 14:09:19 +00:00
Deepesh Garg
5fc8d20e96 chore: label post save
(cherry picked from commit bf7a51791a)
2022-10-23 14:09:18 +00:00
Deepesh Garg
7ddcfc00fc chore: Add patch to update repayment schedule type in loan documents
(cherry picked from commit 679b5ed551)

# Conflicts:
#	erpnext/patches.txt
2022-10-23 14:09:17 +00:00
Deepesh Garg
3f02059085 chore: Update labels as per repayment type
(cherry picked from commit 2ddee50f27)

# Conflicts:
#	erpnext/loan_management/doctype/loan/loan.json
2022-10-23 14:09:14 +00:00
Deepesh Garg
71b21086b1 chore: Remove print statements
(cherry picked from commit 3466461eb3)
2022-10-23 14:09:13 +00:00
Deepesh Garg
c2817bed0b feat: Repayment schedule types for term loans
(cherry picked from commit 76c6ccab5d)

# Conflicts:
#	erpnext/loan_management/doctype/loan/loan.json
#	erpnext/loan_management/doctype/loan/loan.py
2022-10-23 14:09:12 +00:00
Deepesh Garg
dc972718d5 Merge pull request #32687 from frappe/mergify/bp/version-13-hotfix/pr-32650
fix: unset contact details (backport #32650)
2022-10-23 18:32:15 +05:30
barredterra
7afb19f965 fix: unset contact details
(cherry picked from commit 23f0bb45b0)
2022-10-23 12:52:29 +00:00
Deepesh Garg
0ca137808b Merge pull request #32656 from frappe/mergify/bp/version-13-hotfix/pr-32641
fix: allow to create Sales Order from expired Quotation (backport #32641)
2022-10-20 17:34:31 +05:30
rohitwaghchaure
99e0106193 Merge pull request #32670 from frappe/mergify/bp/version-13-hotfix/pr-32667
fix: BOM cost update message (backport #32667)
2022-10-20 16:52:56 +05:30
rohitwaghchaure
447485ae90 fix: conflicts 2022-10-20 15:01:22 +05:30
Rohit Waghchaure
98bcb7255d fix: BOM cost update message
(cherry picked from commit 9cfe527492)

# Conflicts:
#	erpnext/manufacturing/doctype/bom/test_bom.py
2022-10-20 09:09:02 +00:00
Deepesh Garg
2df24ec459 Merge pull request #32664 from frappe/mergify/bp/version-13-hotfix/pr-32659
fix: Billing Address for inter-company purchase docs (backport #32659)
2022-10-20 12:42:38 +05:30
Deepesh Garg
3a2f08fbda fix: Billing Address for inter-company purchase docs
(cherry picked from commit 796f2d3c09)
2022-10-20 06:29:56 +00:00
rohitwaghchaure
7c9b535514 Merge pull request #32661 from frappe/mergify/bp/version-13-hotfix/pr-32654
fix: incorrect qty in material request created from PP (backport #32654)
2022-10-20 10:27:41 +05:30
Rohit Waghchaure
4f0c2909ab test: validate qty and purchase uom in material request which is created from PP
(cherry picked from commit 4d5ef721f7)
2022-10-20 04:30:29 +00:00
Rohit Waghchaure
1741501ea8 fix: incorrect qty in material request
(cherry picked from commit ad278b2007)
2022-10-20 04:30:28 +00:00
Raffael Meyer
ea032893d3 fix: allow to create Sales Order from expired Quotation (#32641)
(cherry picked from commit 4ad3002861)
2022-10-19 16:38:19 +00:00
rohitwaghchaure
7601a016b8 Merge pull request #32653 from frappe/mergify/bp/version-13-hotfix/pr-32645
fix: overlap error not raised for job card in case of workstation with production capacity (backport #32645)
2022-10-19 18:09:44 +05:30
Rohit Waghchaure
b5376ce5cb fix: overlap error not raised for job card in case of workstation with production capacity
(cherry picked from commit 8b2165e0d1)
2022-10-19 11:35:59 +00:00
mergify[bot]
b8d97b82b8 fix: pricing rule item code UOM apply & conversions (backport #32566) (#32636)
* fix: pricing rule for non stock UOM and conversions

* fix: pricing rule for non stock UOM and conversions

(cherry picked from commit 96b4211ea1)

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

* chore: resolve conflicts

Co-authored-by: Maharshi Patel <39730881+maharshivpatel@users.noreply.github.com>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2022-10-18 23:31:31 +05:30
Deepesh Garg
56c4c3186b Merge pull request #32624 from frappe/mergify/bp/version-13-hotfix/pr-32622
fix: Ignore linked purchase invoice on cancel (backport #32622)
2022-10-18 14:49:36 +05:30
Deepesh Garg
015a221d8d chore: Resolve conflicts 2022-10-18 09:11:58 +05:30
Deepesh Garg
eec770a4a8 fix: Ignore linked purchase invoice on cancel
(cherry picked from commit faadf78332)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
2022-10-17 14:09:12 +00:00
Deepesh Garg
44f5ccc9dc Merge pull request #32605 from frappe/mergify/bp/version-13-hotfix/pr-32594
fix: Party account for multi-order invoices (backport #32594)
2022-10-15 18:25:25 +05:30
Deepesh Garg
0ae2a4f1c4 fix: Party account for multi-order invoices
(cherry picked from commit fd49503ba2)
2022-10-15 11:29:21 +00:00
mergify[bot]
5fdaddad86 chore: drop dead code (backport #32595) (#32597)
chore: drop dead code (#32595)

[skip ci]

(cherry picked from commit 50e9698932)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-10-13 15:27:09 +05:30
Sagar Sharma
03ccddc1cc Merge pull request #32587 from frappe/mergify/bp/version-13-hotfix/pr-32576
fix: `Brand Defaults` filters (backport #32576)
2022-10-13 14:42:29 +05:30
Sagar Sharma
e00ebcd9e5 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-32576 2022-10-13 14:38:26 +05:30
mergify[bot]
7e7122b668 fix: don't try to update youtube data if disabled in settings (backport #32588) (#32590)
* fix: don't try to update youtube data if disabled in settings (#32588)

fix: cast value from db

[skip ci]

(cherry picked from commit e543dca6a0)

* chore: qualified path

not imported 

[skip ci]

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-10-13 11:57:08 +05:30
Sagar Sharma
b1c70af4db fix: Brand Defaults filters
(cherry picked from commit 7da32c7db3)
2022-10-12 13:53:56 +00:00
Deepesh Garg
ff3bfb060a Merge pull request #32584 from frappe/mergify/bp/version-13-hotfix/pr-32557
fix: Value error on validation of POS invoices with Serial Nos (backport #32557)
2022-10-12 17:17:01 +05:30
rohitwaghchaure
63e087d31e Merge pull request #32580 from frappe/mergify/bp/version-13-hotfix/pr-32563
fix: consider sales rate as incoming rate for transit warehouse in purchase flow (backport #32563)
2022-10-12 17:08:33 +05:30
Deepesh Garg
b62a397397 Merge pull request #32581 from frappe/mergify/bp/version-13-hotfix/pr-32272
fix: Incoming rate precision fixes for intra company transfer (backport #32272)
2022-10-12 17:04:52 +05:30
ruthra kumar
48efcc82b6 test: value error on serial no validation on pos
(cherry picked from commit 9e2bd10d03)
2022-10-12 11:12:09 +00:00
ruthra kumar
4383980bcc fix: value error on pos submit
(cherry picked from commit 4b908ebcd6)
2022-10-12 11:12:08 +00:00
rohitwaghchaure
aa0552d788 fix: linter issue 2022-10-12 16:33:53 +05:30
Deepesh Garg
3b5889ef85 chore: resolve conflicts 2022-10-12 16:30:23 +05:30
rohitwaghchaure
8238b89907 fix: incorrect import 2022-10-12 16:28:31 +05:30
rohitwaghchaure
6ae2f90683 fix: conflct in test purchase receipt 2022-10-12 16:26:52 +05:30
rohitwaghchaure
cb7aef505d fix: conflict 2022-10-12 16:24:50 +05:30
Deepesh Garg
4a8c42d62b chore: check only for inter-company transfers
(cherry picked from commit 9aa5e20ef7)
2022-10-12 10:54:15 +00:00
Deepesh Garg
c67bdcf3c6 chore: fix precision condition
(cherry picked from commit 49601558c6)
2022-10-12 10:54:14 +00:00
Deepesh Garg
7a9e7b66ac chore: Use proper accounts
(cherry picked from commit 1c05c004cd)
2022-10-12 10:54:13 +00:00
Deepesh Garg
151b9d4d7b chore: Increase precision for other doc fields
(cherry picked from commit c8d2181498)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
#	erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
2022-10-12 10:54:13 +00:00
Deepesh Garg
b966f10711 chore: GL Entries for SLE diff
(cherry picked from commit df2a0e265b)
2022-10-12 10:54:11 +00:00
Deepesh Garg
0b48f13873 test: Internal tranfer precision loss test
(cherry picked from commit dc20b21fb5)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
2022-10-12 10:54:10 +00:00
Deepesh Garg
b531a38efe chore: Increase incoming_rate field precision to 6
(cherry picked from commit b31c3bd35d)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
2022-10-12 10:54:08 +00:00
Deepesh Garg
dd26ef96e0 fix: Hanlde rounding loss for internal transfer
(cherry picked from commit 6e47fd54a0)
2022-10-12 10:54:07 +00:00
Deepesh Garg
227ce5f8a2 fix: Incoming rate precision fixes for intra company transfer
(cherry picked from commit 083309c056)
2022-10-12 10:54:06 +00:00
Rohit Waghchaure
acd64ba7c1 fix: test case
(cherry picked from commit 98bf8e1304)
2022-10-12 10:51:22 +00:00
Rohit Waghchaure
6f43133c04 fix: consider outgoingrate while valuation rate calculate
(cherry picked from commit 3266e54e33)
2022-10-12 10:51:22 +00:00
Rohit Waghchaure
f72602ebf3 fix: consider sales rate as incoming rate for transit warehouse in purchase flow
(cherry picked from commit 683a47f7a1)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
#	erpnext/stock/stock_ledger.py
2022-10-12 10:51:21 +00:00
Ankush Menat
002ae8ae13 ci: disable test orchestrator v13 (#32574) 2022-10-12 12:13:51 +05:30
Deepesh Garg
558dc57b94 Merge pull request #32539 from frappe/mergify/bp/version-13-hotfix/pr-32536
fix: PO cancel post advance payment cancel against PO (backport #32536)
2022-10-11 13:58:24 +05:30
Deepesh Garg
cd942d36a8 chore: resolve conflicts 2022-10-11 10:16:31 +05:30
Sagar Sharma
3257533ee3 Merge pull request #32544 from frappe/mergify/bp/version-13-hotfix/pr-32497
chore: set `Quality Inspection` status based on readings status (backport #32497)
2022-10-10 12:10:29 +05:30
Sagar Sharma
2763d06307 fix(test): test_rejected_qi_validation
(cherry picked from commit 4992e4a2b8)
2022-10-10 05:21:09 +00:00
Sagar Sharma
a0772ea8d7 test: add test cases for Quality Inspection status
(cherry picked from commit fcc1272d42)
2022-10-10 05:21:08 +00:00
Sagar Sharma
d67b44fcec fix: set Quality Inspection status based on readings status
(cherry picked from commit 2657ece2cd)
2022-10-10 05:21:08 +00:00
Sagar Sharma
257a2a3d71 fix: make readings status mandatory in Quality Inspection
(cherry picked from commit d7c3b7633a)
2022-10-10 05:21:07 +00:00
Sagar Sharma
e6abbd1c83 chore: add Manual Inspection field in Quality Inspection DocType
(cherry picked from commit 39707757a6)
2022-10-10 05:21:06 +00:00
Deepesh Garg
41599cf29f fix: PO cancel post advance payment cancel against PO
(cherry picked from commit d806e32030)

# Conflicts:
#	erpnext/buying/doctype/purchase_order/purchase_order.py
2022-10-09 13:05:46 +00:00
Deepesh Garg
997990c69f Merge pull request #32523 from frappe/mergify/bp/version-13-hotfix/pr-32522
fix: Tax withholding related fixes (backport #32522)
2022-10-07 18:08:31 +05:30
Deepesh Garg
2bf76f64bd chore: resolve conflicts 2022-10-07 17:33:03 +05:30
Deepesh Garg
e1c41b9195 fix: Do not add tax withheld vouchers post tax withheding in one document
(cherry picked from commit 781d160c68)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
2022-10-07 11:03:16 +00:00
Deepesh Garg
32a9575f07 fix: Tax withholding related fixes
(cherry picked from commit abf5b6be3e)
2022-10-07 11:03:15 +00:00
Deepesh Garg
e031e095dd Merge pull request #32521 from FHenry/13_fr_translation
chore: fr translation (backport #32385)
2022-10-07 08:48:43 +05:30
Florian HENRY
39ce3756c8 chore: fr translation 2022-10-06 22:39:40 +02:00
Deepesh Garg
f2515d9c9c Merge pull request #32449 from rtdany10/cst-ux-improvement
feat(ux): improved course scheduling tool
2022-10-05 18:44:05 +05:30
Deepesh Garg
27fe9c9179 Merge pull request #32426 from AnandBaburajan/fix_mark_attendance_joining_date_and_future
fix: mark attendance issue with joining and relieving date, and fix future attendance marking
2022-10-05 18:34:31 +05:30
Deepesh Garg
503183cb0b Merge pull request #32502 from frappe/mergify/bp/version-13-hotfix/pr-32499
fix: TooManyWritesError during reposting of stock (backport #32499)
2022-10-05 17:29:25 +05:30
Deepesh Garg
f1ea5de022 Merge pull request #32504 from frappe/mergify/bp/version-13-hotfix/pr-32478
feat(JE): trigger account field when fetched from template (backport #32478)
2022-10-05 17:29:05 +05:30
Dany Robert
baa4fec611 feat(JE): trigger account field when fetched from template
Closes #32409

(cherry picked from commit c35adcf5a1)
2022-10-05 10:38:21 +00:00
Rohit Waghchaure
476175b307 fix: TooManyWritesError during reposting of stock
(cherry picked from commit aaabba9b1e)
2022-10-05 10:35:55 +00:00
rohitwaghchaure
3648745e5e Merge pull request #32470 from frappe/mergify/bp/version-13-hotfix/pr-32466
fix: not able to return sold expired batches (backport #32466)
2022-10-04 07:35:10 +05:30
Deepesh Garg
8e1e6a194b Merge pull request #32467 from frappe/mergify/bp/version-13-hotfix/pr-32394
fix: fetch swift number in payment request from bank doctype (backport #32394)
2022-10-03 21:54:05 +05:30
Ankush Menat
663e9b403f chore: codeowners 2022-10-03 16:27:32 +05:30
Ankush Menat
3219b7c766 chore: codeowners 2022-10-03 16:27:13 +05:30
Sagar Sharma
3a6e4c8da7 Merge pull request #32474 from frappe/mergify/bp/version-13-hotfix/pr-32472
fix: pick list picked-qty for batch item (backport #32472)
2022-10-03 15:24:02 +05:30
Sagar Sharma
96a6db0422 fix: pick list picked-qty for batch item
(cherry picked from commit ba02209f1d)
2022-10-03 09:17:03 +00:00
rohitwaghchaure
4757a65863 fix: linter issue 2022-10-03 13:27:38 +05:30
rohitwaghchaure
0f8e34e972 fix: conflict 2022-10-03 13:18:58 +05:30
Rohit Waghchaure
2fd4485f2f fix: not able to return sold expired batches
(cherry picked from commit 0b1727cf79)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
2022-10-03 07:46:13 +00:00
Maharshi Patel
299175e6fe fix: payment request make bank field Link instead of Read Only
(cherry picked from commit dc8d49260c)
2022-10-03 07:26:06 +00:00
Maharshi Patel
8251b843ec Revert "fix: fetch swift_number in payment_request"
This reverts commit f42a8e4e03.

(cherry picked from commit 9245d3b5cd)
2022-10-03 07:26:05 +00:00
Maharshi Patel
60745705a8 fix: fetch swift_number in payment_request
There isn't direct link between payment_request and bank so swift_number wasn't fetched using Fetch form. I fixed it by fetching swift_number on_change of bank_account.

(cherry picked from commit f42a8e4e03)
2022-10-03 07:26:04 +00:00
Deepesh Garg
caedc9f6ad Merge pull request #32462 from frappe/mergify/bp/version-13-hotfix/pr-32456
fix: update with new Frappe color. fix #32455 (backport #32456)
2022-10-03 09:15:21 +05:30
Deepesh Garg
fbeb86b9c0 Merge pull request #32453 from deepeshgarg007/intra_state_transfer_eway_bill_v13
fix: e-Way bill JSON for Intra-state internal transfers
2022-10-03 09:07:26 +05:30
Muvuk
ad43b18ace fix: update with new Frappe color. fix #32455 (#32456)
* Update with new Frappe color.

* refactor: use css variables

Co-authored-by: Ankush Menat <ankushmenat@gmail.com>
(cherry picked from commit 73e5a7d671)
2022-10-02 17:02:25 +00:00
Deepesh Garg
9f8d3e43ea Merge pull request #32413 from AnandBaburajan/fix_asset_sold_status_v13
fix: status of assets with maintenance_required changing back to 'Partially Depreciated' some time after being sold [v13]
2022-10-02 17:43:09 +05:30
Deepesh Garg
9dbb2bf512 Merge pull request #32369 from maharshivpatel/fix-taxes-sez-without-payment-of-tax
fix: SEZ Without Payment of Tax don't add tax rows
2022-10-01 16:13:11 +05:30
Deepesh Garg
8c52c71299 Merge pull request #32344 from maharshivpatel/fix-payment-reconciliation-jv-cost-center
fix: consider cost center for journal entry in payment reconciliation tool
2022-10-01 16:12:30 +05:30
Deepesh Garg
c4c52c1cd3 chore: Linting Issues 2022-10-01 16:06:31 +05:30
Deepesh Garg
b7fbf75e10 fix: e-Way bill JSON for Intra-state internal transfers 2022-10-01 15:58:31 +05:30
Dany Robert
6dabfbedf1 Merge branch 'version-13-hotfix' into cst-ux-improvement 2022-10-01 14:19:31 +05:30
Dany Robert
68c592377b chore: pre-commit 2022-09-30 23:41:01 -07:00
Deepesh Garg
d69d2217f6 Merge pull request #32431 from frappe/mergify/bp/version-13-hotfix/pr-32402
fix: Item details fetching on making transaction from item dashboard (backport #32402)
2022-10-01 12:05:26 +05:30
mergify[bot]
cdc8297083 fix: typo in sales_register's filter mode_of_payment (backport #32371) (#32447)
fix: typo in sales_register's filter mode_of_payment (#32371)
2022-10-01 12:04:40 +05:30
Dany Robert
f24f71f387 Merge branch 'version-13-hotfix' into cst-ux-improvement 2022-10-01 12:03:05 +05:30
Dany Robert
552c5951bd feat: cst ux improvement 2022-09-30 23:25:55 -07:00
Deepesh Garg
562025eb45 Merge branch 'version-13-hotfix' into fix-payment-reconciliation-jv-cost-center 2022-10-01 11:10:21 +05:30
Deepesh Garg
f0a37f545a chore: resolve conflicts 2022-10-01 11:02:40 +05:30
Deepesh Garg
001afb96ba Merge pull request #32435 from frappe/mergify/bp/version-13-hotfix/pr-32412
fix: Incorrect TCS amount deducted in Sales Invoice (backport #32412)
2022-10-01 11:01:05 +05:30
Deepesh Garg
61d3370527 chore: Remove print statements
(cherry picked from commit bff3cd9068)
2022-09-30 10:36:20 +00:00
Deepesh Garg
32456bff30 fix: Incorrect TCS amount deducted in Sales Invoice
(cherry picked from commit 08443c6421)
2022-09-30 10:36:20 +00:00
Deepesh Garg
1aed76f778 fix: Item details fetching on making transaction from item dashboard
(cherry picked from commit 0439e41a44)

# Conflicts:
#	erpnext/stock/doctype/item/item.js
2022-09-30 10:27:36 +00:00
anandbaburajan
308c400c6a chore: make linter happy 2022-09-30 15:33:34 +05:30
anandbaburajan
a9546dd01f fix: mark attendance issue with relieving date 2022-09-30 15:27:03 +05:30
anandbaburajan
cb4fbd5432 fix: future attendance marking 2022-09-30 14:48:30 +05:30
anandbaburajan
6f8d62088e fix: mark attendance issue with joining date 2022-09-30 14:46:57 +05:30
Anand Baburajan
5d66646fcd Merge branch 'version-13-hotfix' into fix_asset_sold_status_v13 2022-09-29 22:49:10 +05:30
Sagar Sharma
6a58d15497 Merge pull request #32419 from frappe/mergify/bp/version-13-hotfix/pr-32404
fix(ux): show `Make Purchase Invoice` button based on permission (backport #32404)
2022-09-29 17:16:15 +05:30
anandbaburajan
6b21deedae chore: refactor by just using a filter 2022-09-29 15:37:04 +05:30
Sagar Sharma
7124328640 fix: show Make Purchase Invoice button based on permission
(cherry picked from commit 80080a3d7b)
2022-09-29 09:27:03 +00:00
anandbaburajan
430a4c98a0 chore: refactor by creating is_sold 2022-09-29 08:59:49 +05:30
Anand Baburajan
c65fb64578 Merge branch 'version-13-hotfix' into fix_asset_sold_status_v13 2022-09-28 21:36:48 +05:30
anandbaburajan
14ab9d9158 fix: asset requiring maintenance sold status 2022-09-28 21:25:42 +05:30
Deepesh Garg
1095052479 Merge pull request #32410 from frappe/mergify/bp/version-13-hotfix/pr-32403
fix: Disbursement Account in patch to update old loans (backport #32403)
2022-09-28 20:13:45 +05:30
Deepesh Garg
b4a511cbb4 fix: Disbursement Account in patch to update old loans
(cherry picked from commit be623ce8e8)
2022-09-28 14:06:42 +00:00
Deepesh Garg
0b2405bbdf Merge pull request #32395 from frappe/mergify/bp/version-13-hotfix/pr-32379
fix: POS only validate QTY if is_stock_item (backport #32379)
2022-09-28 15:12:47 +05:30
Rucha Mahabal
255aa7a84a fix: don't count half day in absent days in Monthly Attendance Sheet summarized view (#32399) 2022-09-28 15:02:21 +05:30
Deepesh Garg
a604eed1b9 chore: Resolve conflicts 2022-09-28 14:49:25 +05:30
Maharshi Patel
96fa14be88 fix: POS properly validate stock for bundle products
Stock availability was not calculated properly for Product Bundle with non stock item so i have added logic to properly calculate that as well.

(cherry picked from commit e392ea1104)

# Conflicts:
#	erpnext/selling/page/point_of_sale/pos_item_details.js
2022-09-28 08:16:02 +00:00
Maharshi Patel
ac8100f1e5 fix: POS only validate QTY if is_stock_item
POS invoice raised " Item not available " validation error even though item is non_stock.

(cherry picked from commit e39e088f18)
2022-09-28 08:16:00 +00:00
Deepesh Garg
07cc05785e Merge pull request #32386 from frappe/mergify/bp/version-13-hotfix/pr-32382
fix: Move subscription process to hourly long queue (backport #32382)
2022-09-28 10:39:08 +05:30
Deepesh Garg
f03fbc0e6d chore: Resolve conflicts 2022-09-28 08:10:37 +05:30
Deepesh Garg
447c553954 fix: Move subscription process to hourly long quque
(cherry picked from commit 82a2f31ada)

# Conflicts:
#	erpnext/hooks.py
2022-09-27 18:09:32 +00:00
Deepesh Garg
b2e9dccc8c Merge pull request #32384 from frappe/mergify/bp/version-13-hotfix/pr-32378
fix: Add return against indexes for POS Invoice (backport #32378)
2022-09-27 23:34:50 +05:30
Deepesh Garg
9e8e7aab70 fix: Add return against indexes for POS Invoice
(cherry picked from commit cbfe28286a)
2022-09-27 16:52:59 +00:00
Deepesh Garg
6c528d469d fix: Add return against indexes for POS Invoice
(cherry picked from commit 1f6205e1ea)
2022-09-27 16:52:59 +00:00
Maharshi Patel
988c5b95e6 fix: GST Itemised Sales Register GSTIN filter (#32367)
fix: GST Itemised Sales Register GSTIN filte
2022-09-27 22:21:14 +05:30
Sagar Sharma
1f2887d601 Merge pull request #32381 from frappe/mergify/bp/version-13-hotfix/pr-32377
fix: consider overproduction percentage for WO finish button (backport #32377)
2022-09-27 18:51:37 +05:30
Sagar Sharma
ed4ac100ba fix: consider overproduction percentage for WO finish button
(cherry picked from commit 05392e0918)
2022-09-27 12:15:43 +00:00
Sagar Sharma
a194d28b69 fix: For Quantity error msg in Stock Entry
(cherry picked from commit 9049db41ae)
2022-09-27 12:15:42 +00:00
rohitwaghchaure
3217924242 Merge pull request #32373 from frappe/mergify/bp/version-13-hotfix/pr-32370
fix: Not allowing to return expired batches using purchase return (backport #32370)
2022-09-27 14:44:30 +05:30
Rohit Waghchaure
02468a902f fix: allow to return expired batches using purchase return
(cherry picked from commit a4a86ee23f)
2022-09-27 08:51:26 +00:00
Maharshi Patel
a13eecc961 fix: SEZ Without Payment of Tax don't add tax rows
taxes were added even when gst_category was SEZ and export_type was Without Payment of Tax
2022-09-27 13:40:04 +05:30
rohitwaghchaure
776ee53a25 Merge pull request #32358 from frappe/mergify/bp/version-13-hotfix/pr-32346
refactor: rewrite `Item Price Stock Report` queries in `QB` (backport #32346)
2022-09-27 10:44:05 +05:30
rohitwaghchaure
ddf5565c67 Merge pull request #32366 from frappe/mergify/bp/version-13-hotfix/pr-32339
fix: opening entry causing discrepancy between stock and trial balance (backport #32339)
2022-09-27 09:57:01 +05:30
Maharshi Patel
4152a9f026 Merge branch 'version-13-hotfix' into fix-payment-reconciliation-jv-cost-center 2022-09-26 23:03:39 +05:30
Saqib Ansari
8f961abe8b fix(e-invoicing): local variable 'res' referenced before assignment (#32352) 2022-09-26 22:42:07 +05:30
Rohit Waghchaure
70c68f011a fix: opening entry causing discepancy between stock and trial balance
(cherry picked from commit bc3ab45af2)
2022-09-26 15:03:48 +00:00
Sagar Sharma
9066009e89 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-32346 2022-09-26 18:18:41 +05:30
Sagar Sharma
9332e7f96f Merge pull request #32360 from frappe/mergify/bp/version-13-hotfix/pr-32347
refactor: rewrite `Incorrect Stock Value Report` queries in `QB` (backport #32347)
2022-09-26 18:18:01 +05:30
Sagar Sharma
d6a335e59f refactor: rewrite Incorrect Stock Value Report queries in QB
(cherry picked from commit b93331e844)
2022-09-26 11:40:21 +00:00
Sagar Sharma
a66002f774 refactor: rewrite Item Price Stock Report queries in QB
(cherry picked from commit 22299d2382)
2022-09-26 11:39:24 +00:00
ruthra kumar
6eb3428f8e Merge pull request #32348 from frappe/mergify/bp/version-13-hotfix/pr-32303
fix: difference amount calculation and popup on payment reconciliation (backport #32303)
2022-09-26 14:18:09 +05:30
ruthra kumar
d158784472 Merge pull request #32350 from frappe/mergify/bp/version-13-hotfix/pr-32310
fix: total value in all keys (backport #32310)
2022-09-26 12:05:04 +05:30
nishibakabeer
80d046a38c fix: total value in all keys
Gross and net profit report showing wrong values in monthly quarterly and half yearly filters which is the total value
@ruthra-kumar added in develop branch as suggested ( https://github.com/frappe/erpnext/pull/32020)

(cherry picked from commit 6919f389aa)
2022-09-26 06:03:15 +00:00
ruthra kumar
d68cdb4f5e fix: difference amount calculation on payment reconciliation
(cherry picked from commit 122d5f2729)
2022-09-26 10:36:08 +05:30
Maharshi Patel
9f988910b5 fix: payment reconciliation tool consider cost_center for JV
We need to consider cost center in all four cases of get_conditions. I have removed check in if statement for get_invoices, get_payments, get_return_invoices as it is not required.
2022-09-25 23:24:59 +05:30
Deepesh Garg
09cd480c81 Merge pull request #32333 from frappe/mergify/bp/version-13-hotfix/pr-32117
fix: Reduce font size for Process Statement of accounts print/pdf (backport #32117)
2022-09-25 17:20:57 +05:30
Sagar Sharma
4777991a74 Merge pull request #32338 from frappe/mergify/bp/version-13-hotfix/pr-32324
refactor: rewrite `Production Planning Report` queries in `QB` (backport #32324)
2022-09-23 10:38:20 +05:30
Sagar Sharma
1301a6ff7f refactor: rewrite Production Planning Report queries in QB
(cherry picked from commit 8417b9b99c)
2022-09-23 04:20:42 +00:00
Sagar Sharma
a6a5f63af2 Merge pull request #32328 from s-aga-r/backport/v13-h/32309
fix: item_code key error in production plan (backport #32309)
2022-09-23 09:48:25 +05:30
Deepesh Garg
1cfeb9371c fix: Reduce font size for Process Statement of accounts print/pdf
(cherry picked from commit 6bfd193b0d)
2022-09-22 19:38:55 +00:00
Rohit Waghchaure
638d5e9dc3 fix: item_code key error in production plan 2022-09-22 18:09:12 +05:30
Sagar Sharma
b4ee72e15e Merge pull request #32316 from s-aga-r/backport/v13-h/32304
refactor: rewrite `Exponential Smoothing Forecasting` queries in `QB` (backport #32304)
2022-09-22 11:16:22 +05:30
Sagar Sharma
2a9b519773 Merge branch 'version-13-hotfix' into backport/v13-h/32304 2022-09-22 01:32:20 +05:30
Sagar Sharma
57136fa921 Merge pull request #32315 from s-aga-r/backport/v13-h/32153
refactor: rewrite Work Order Stock Report queries in QB (backport #32153)
2022-09-22 01:31:18 +05:30
Sagar Sharma
882542c2d5 Merge branch 'version-13-hotfix' into backport/v13-h/32153 2022-09-22 01:31:04 +05:30
Sagar Sharma
0a4025e7e0 Merge pull request #32314 from s-aga-r/backport/v13-h/32161
refactor: rewrite Process Loss Report queries in QB (backport #32161)
2022-09-22 01:30:40 +05:30
Sagar Sharma
54dfd50391 refactor: rewrite Exponential Smoothing Forecasting queries in QB 2022-09-21 21:27:04 +05:30
Sagar Sharma
45d02ceb4e refactor: rewrite Work Order Stock Report queries in QB 2022-09-21 21:20:03 +05:30
Sagar Sharma
f72bb18da7 refactor: rewrite Process Loss Report queries in QB 2022-09-21 21:13:10 +05:30
Deepesh Garg
f371008cd9 Merge pull request #32293 from frappe/mergify/bp/version-13-hotfix/pr-32284
fix: remove no_copy for ignore_pricing_rule (backport #32284)
2022-09-21 16:41:25 +05:30
Deepesh Garg
82e5a784cb Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-32284 2022-09-21 13:08:41 +05:30
Deepesh Garg
d267372334 Merge pull request #32299 from frappe/mergify/bp/version-13-hotfix/pr-32296
fix: get amount in words for debit note (backport #32296)
2022-09-21 13:07:39 +05:30
Sagar Sharma
92c7d074dd Merge pull request #32307 from s-aga-r/backport/13/refactor/report/exponential-smoothing-forecasting
refactor: rewrite `BOM Variance Report` queries in `QB` (backport #32297)
2022-09-21 12:42:10 +05:30
Sagar Sharma
cedc8d28e4 refactor: rewrite BOM Variance Report queries in QB 2022-09-21 12:13:05 +05:30
Deepesh Garg
d0bd78ddcd Merge pull request #32289 from frappe/mergify/bp/version-13-hotfix/pr-32204
fix(UX): More predictable tax withholding application in invoices (backport #32204)
2022-09-21 00:11:37 +05:30
Deepesh Garg
e2912caeae chore: Handle edge cases 2022-09-20 23:50:16 +05:30
Sagar Sharma
6f9c2e6c80 Merge pull request #32302 from frappe/mergify/bp/version-13-hotfix/pr-32295
refactor: rewrite `BOM Stock Report` queries in `QB` (backport #32295)
2022-09-20 23:10:19 +05:30
Sagar Sharma
96bf1e2a0a fix: warehouse filter in BOM Stock Calculated Report
(cherry picked from commit 390ce5719d)
2022-09-20 17:37:41 +00:00
Sagar Sharma
1f633b293d refactor: rewrite BOM Stock Report queries in QB
(cherry picked from commit 8fd7c04920)
2022-09-20 17:37:40 +00:00
ruthra kumar
0cd1cacc29 fix: get amount in words for debit note
(cherry picked from commit 70f6484d9d)
2022-09-20 12:43:45 +00:00
Deepesh Garg
21154c8bee chore: fix tests 2022-09-20 17:52:37 +05:30
ruthra kumar
9270e58969 Merge pull request #32277 from frappe/remove_return_pos_from_reconciliation_tool
fix: remove return pos from pos reconciliation tool
2022-09-20 17:20:25 +05:30
Deepesh Garg
de8f44bbff chore: Resolve conflicts 2022-09-20 16:42:30 +05:30
ruthra kumar
e9bf74e589 fix: remove return pos from pos reconciliation tool 2022-09-20 16:41:28 +05:30
Maharshi Patel
8abfdb6598 fix: remove no_copy for ignore_pricing_rule
(cherry picked from commit 8c5b420aea)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
#	erpnext/buying/doctype/purchase_order/purchase_order.json
#	erpnext/selling/doctype/quotation/quotation.json
#	erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
2022-09-20 10:49:59 +00:00
Nabin Hait
305693b562 Merge pull request #32281 from maharshivpatel/fix-item-wise-sales-register-v13
fix: item wise sales register taxes and charges
2022-09-20 14:56:23 +05:30
Deepesh Garg
794eeebabc chore: resolve conflicts 2022-09-20 14:48:06 +05:30
Deepesh Garg
75768d780f Merge pull request #32282 from frappe/mergify/bp/version-13-hotfix/pr-32217
fix: incorrect gl if tax on multi currency payment entry (backport #32217)
2022-09-20 14:39:37 +05:30
Deepesh Garg
fe252b48f7 chore: fix tests
(cherry picked from commit 9aa1f84d45)
2022-09-20 09:07:06 +00:00
Deepesh Garg
ca0cce7599 fix: TDS deduction via journal entry
(cherry picked from commit 36d0906ea2)
2022-09-20 09:07:05 +00:00
Deepesh Garg
e07fd46a46 test: Add tests
(cherry picked from commit b6184ce471)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
2022-09-20 09:07:04 +00:00
Deepesh Garg
51d7c0bfe4 fix: Fetch vouchers to show in Invoice
(cherry picked from commit 3fb1595a4e)
2022-09-20 09:07:03 +00:00
Deepesh Garg
4165fee6a7 fix: Add child table for tax withheld vouchers
(cherry picked from commit 246c1a9380)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
2022-09-20 09:07:02 +00:00
Deepesh Garg
d251ef9ce5 Merge pull request #32279 from frappe/mergify/bp/version-13-hotfix/pr-32235
fix: fetch description only if empty on the payment schedule (backport #32235)
2022-09-20 14:35:35 +05:30
Sagar Sharma
afda4bdb17 Merge pull request #32286 from frappe/mergify/bp/version-13-hotfix/pr-32280
refactor: rewrite `Item Shortage Report` queries in QB (backport #32280)
2022-09-20 11:43:41 +05:30
Sagar Sharma
c4452360da test: add test cases for Item Shortage Report
(cherry picked from commit 3dc754cac2)
2022-09-20 05:45:29 +00:00
Sagar Sharma
13527d6154 refactor: rewrite Item Shortage Report queries in QB
(cherry picked from commit f0a78aa559)
2022-09-20 05:45:28 +00:00
ruthra kumar
5c0a46110e test: gl entries of payments with advance tax
(cherry picked from commit 5bd5dd7262)
2022-09-20 11:09:37 +05:30
ruthra kumar
d9d6a07bbb fix: incorrect gl if tax on multi currency payment entry
(cherry picked from commit f0ae77b23b)
2022-09-20 04:30:42 +00:00
Maharshi Patel
e65990eb34 fix: item wise sales register taxes and charges
I have added a separate column for other charges. Instead of adding all values to tax_total, it checks if account_type is tax, and then only it adds to total_tax otherwise it adds to the total_other_charges.
2022-09-20 09:47:47 +05:30
Maharshi Patel
07a7fdbe6c fix: fetch description only if empty on the payment schedule
added fetch_if_empty on description field of payment_schedule.

(cherry picked from commit f4b64686ae)
2022-09-20 03:44:50 +00:00
Sagar Sharma
2fca8b541e Merge pull request #32261 from frappe/mergify/bp/version-13-hotfix/pr-32250
fix: make `po_detail` or `sco_rm_detail` mandatory for SE Send to Subcontractor (backport #32250)
2022-09-19 19:53:57 +05:30
Sagar Sharma
7fc460bb32 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-32250 2022-09-19 19:22:31 +05:30
Solufyin
e2e69dced7 fix: Change Department fieldtype to Link in Leave Balance Report (#32252)
Co-authored-by: Nihantra C. Patel <n.patel.serpentcs@gmail.com>
2022-09-19 15:42:05 +05:30
Deepesh Garg
5e49b6ea0f Merge pull request #32269 from frappe/mergify/bp/version-13-hotfix/pr-32251
fix: use default supplier currency if default supplier is enabled (backport #32251)
2022-09-19 15:20:44 +05:30
Ritwik Puri
2e3445fad9 chore: patch for removing stale values in Naming Series (#32271) 2022-09-19 13:35:08 +05:30
ruthra kumar
3c10e5066a fix: use default supplier currency if default supplier is enabled
(cherry picked from commit 77fdc37cb7)
2022-09-19 07:32:58 +00:00
Sagar Sharma
e8d2e49155 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-32250 2022-09-18 20:30:21 +05:30
Deepesh Garg
5690e9771a Merge pull request #32256 from frappe/mergify/bp/version-13-hotfix/pr-32244
fix: Parent Level project linkning on creating PO from project (backport #32244)
2022-09-18 19:41:57 +05:30
Sagar Sharma
ec6cac8043 chore: conflicts 2022-09-18 16:38:09 +05:30
Sagar Sharma
0329642116 fix: make po_detail or sco_rm_detail mandatory for SE Send to Subcontractor
(cherry picked from commit b90875575c)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/stock_entry.py
2022-09-18 09:25:10 +00:00
Deepesh Garg
3c3e3cfcf8 fix: Parent Level project linkning on creating PO from project
(cherry picked from commit 93e134aab0)
2022-09-17 15:00:56 +00:00
Sagar Sharma
7c4f5fa5c5 Merge pull request #32241 from frappe/mergify/bp/version-13-hotfix/pr-32236
fix: production plan pending-qty (backport #32236)
2022-09-16 20:43:28 +05:30
Sagar Sharma
367e05f808 test: update test case for production plan pending-qty
(cherry picked from commit bd6af7c613)
2022-09-16 13:00:59 +00:00
Sagar Sharma
2dcb35da33 fix: production plan pending-qty
(cherry picked from commit 5be7d42dfd)
2022-09-16 13:00:58 +00:00
Sagar Sharma
94732479f5 Merge pull request #32237 from frappe/mergify/bp/version-13-hotfix/pr-32233
refactor: rewrite Production Plan queries in QB (backport #32233)
2022-09-16 17:35:38 +05:30
Sagar Sharma
b4dec4630d chore: conflicts 2022-09-16 17:08:35 +05:30
Sagar Sharma
c415a47d25 refactor: rewrite Production Plan queries in QB
(cherry picked from commit b8cf3b4c77)

# Conflicts:
#	erpnext/manufacturing/doctype/production_plan/production_plan.py
2022-09-16 09:30:11 +00:00
rohitwaghchaure
1e0cc65c61 Merge pull request #32220 from frappe/mergify/bp/version-13-hotfix/pr-31681
fix: dont show zero qty available items in stock ageing report (backport #31681)
2022-09-15 12:39:22 +05:30
Rohit Waghchaure
37dbc7043a fix: dont show zero qty available items in stock ageing report
(cherry picked from commit 5da7e01db2)
2022-09-15 06:01:10 +00:00
Deepesh Garg
0e88496607 Merge pull request #32209 from frappe/mergify/bp/version-13-hotfix/pr-32208
fix: Loans pending accrual entries (backport #32208)
2022-09-14 15:34:10 +05:30
Abhinav Raut
ef86b437cb fix: pending accrual entries
(cherry picked from commit f2209045f8)
2022-09-14 08:45:27 +00:00
Deepesh Garg
b7b0076743 Merge pull request #32194 from deepeshgarg007/loan_amount_post_write_off
fix: Loan amount post write off
2022-09-13 13:41:21 +05:30
Deepesh Garg
ef5dd1d693 Merge pull request #32165 from frappe/mergify/bp/version-13-hotfix/pr-32144
fix: Rate for internal PI have non stock UOM items (backport #32144)
2022-09-13 12:02:12 +05:30
Deepesh Garg
a53b40ba93 chore: Remove print 2022-09-13 12:00:06 +05:30
Deepesh Garg
e78a7679a4 fix: Loan amount post write off 2022-09-13 11:53:11 +05:30
Deepesh Garg
6a5beecb36 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-32144 2022-09-13 09:39:37 +05:30
Rucha Mahabal
2b900e2f0e Merge pull request #32187 from ruchamahabal/stat-comp-pd
fix: calculate amount based on payment days for statistical components too
2022-09-13 02:09:39 +05:30
Rucha Mahabal
bc14dbbcad refactor(tests): add a test utility file for commonly used functions in tests 2022-09-13 01:42:27 +05:30
Rucha Mahabal
f4e53a5c91 test: statistical component based on payment days 2022-09-13 00:59:59 +05:30
Rucha Mahabal
3f99522764 fix: calculate amount based on payment days for statistical components too 2022-09-13 00:59:03 +05:30
Sagar Sharma
81dd9722fe Merge pull request #32179 from Havenir/fix-picklist-picked-qty
fix: pick_list - picked qty getting set to 1
2022-09-12 22:19:26 +05:30
Sagar Sharma
9c5d335360 Merge pull request #32186 from frappe/mergify/bp/version-13-hotfix/pr-32150
refactor: BOM Stock Calculated report, fix required-qty (backport #32150)
2022-09-12 19:48:38 +05:30
Sagar Sharma
c5c28615f5 test: add test cases for BOM Stock Calculated report
(cherry picked from commit e1a98c1ff7)
2022-09-12 13:52:31 +00:00
Sagar Sharma
ab32c09ff9 fix: add missing warehouse filter in BOM Stock Calculated report
(cherry picked from commit 7a968a5f0d)
2022-09-12 13:52:30 +00:00
Sagar Sharma
f2e63bc491 fix: required_qty in BOM Stock Calculated report
(cherry picked from commit 56192daabf)
2022-09-12 13:52:30 +00:00
Sagar Sharma
882aa96973 refactor: BOM Stock Calculated report
(cherry picked from commit 723fa9eebc)
2022-09-12 13:52:29 +00:00
Rucha Mahabal
d29a033c09 fix(Salary Slip): set default amount to 0 if None (#32184) 2022-09-12 19:21:58 +05:30
Deepesh Garg
b3125a56ed Merge pull request #32178 from deepeshgarg007/manual_update_loan_amount
feat: Ability to manually update loan amount in Salary Slips
2022-09-12 17:41:02 +05:30
rohitwaghchaure
adfc57487b Merge pull request #32167 from frappe/mergify/bp/version-13-hotfix/pr-32118
fix: option to start reposting from repost item valuation (backport #32118)
2022-09-12 16:16:26 +05:30
Deepesh Garg
ac320e4d55 feat: Ability to manually update loan amount in Salary Slips 2022-09-12 15:05:42 +05:30
Ahmad
3256e2b8b7 fix: pick_list - picked qty getting set to 1 2022-09-12 14:23:44 +05:00
mergify[bot]
b4ec4ccc56 chore: correct license text for GPLv3 (backport #32170) (#32172)
* chore: correct license text for GPLv3 (#32170)

[skip ci]



Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-09-12 14:00:14 +05:30
Rohit Waghchaure
a1826f215a fix: option to start reposting from repost item valuation
(cherry picked from commit f1c4aea7b5)
2022-09-12 06:44:59 +00:00
Deepesh Garg
181c0acf99 Merge pull request #32127 from SolufyPrivateLimited/Solufy-so-to-po-v13
fix: Purchase Order creation from Sales Order
2022-09-12 09:25:24 +05:30
Deepesh Garg
638d6b7177 fix: Rate for internal PI have non stock UOM items
(cherry picked from commit 0f655e4430)
2022-09-12 03:40:23 +00:00
rohitwaghchaure
f5132411eb Merge pull request #32152 from frappe/mergify/bp/version-13-hotfix/pr-32135
fix: reposting not working for internal transferred purchase receipt (backport #32135)
2022-09-10 23:29:56 +05:30
rohitwaghchaure
e177f1e51b fix: linter 2022-09-10 23:01:48 +05:30
rohitwaghchaure
6f2a567c95 fix: conflict 2022-09-10 16:41:39 +05:30
Rohit Waghchaure
bb41d8bc47 fix: reposting not working for internal transferred purchase receipt
(cherry picked from commit a03b4ce213)

# Conflicts:
#	erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
2022-09-09 17:13:21 +00:00
Nihantra C. Patel
d79aacd1cb fix: Purchase Order creation from Sales Order 2022-09-08 13:51:11 +05:30
Sagar Sharma
44c3a322d3 fix: require barcode item barcode (#32112)
fix: require barcode item barcode. (#31957)

* fix: require barcode item barcode.

* fix: make supplier mandatory in Item Supplier DocType

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>

Co-authored-by: Devin Slauenwhite <devin.slauenwhite@gmail.com>
2022-09-07 14:24:53 +05:30
Sagar Sharma
5707118d58 Merge pull request #32099 from s-aga-r/fix/stock-entry/send-to-subcontractor
fix: consider Stock Entry purpose while getting total supplied qty
2022-09-06 15:49:38 +05:30
Sagar Sharma
8964612b8e fix: consider Stock Entry purpose while getting total supplied qty 2022-09-06 13:04:03 +05:30
Deepesh Garg
514d280ee7 Merge pull request #32086 from Havenir/qr_code_multi_currency_issue
fix: QR Code multi currency issue
2022-09-05 12:42:43 +05:30
hamzaali15
b10a2b87b6 fix: QR Code multi currency issue
When try to scan qr code on app it is showing correct values for multi currencies because it is not getting base amount
2022-09-05 11:15:43 +05:00
Deepesh Garg
3664a12bb9 Merge pull request #32078 from frappe/mergify/bp/version-13-hotfix/pr-31954
feat: better Item Price list view (backport #31954)
2022-09-04 18:15:13 +05:30
Deepesh Garg
0605cbf26b Merge pull request #32076 from frappe/mergify/bp/version-13-hotfix/pr-32045
fix: Naming series in Journal Entry Template (backport #32045)
2022-09-04 18:14:40 +05:30
HENRY Florian
25d2847881 feat: better Item Price list view (#31954)
* feat: better Item Price list view

(cherry picked from commit 86395c6adb)
2022-09-04 12:38:43 +00:00
Solufyin
66cb3fd63f fix: Naming series in Journal Entry Template
(cherry picked from commit 2085626390)
2022-09-04 11:04:44 +00:00
Deepesh Garg
d22104ad40 Merge pull request #32072 from frappe/mergify/bp/version-13-hotfix/pr-31822
fix(pos): error while consolidating pos invoices (backport #31822)
2022-09-04 15:57:37 +05:30
Saqib Ansari
0527393b09 fix(pos): error while consolidating pos invoices
(cherry picked from commit 33762dbbac)
2022-09-04 07:56:06 +00:00
Deepesh Garg
741d6fcb9a Merge pull request #32053 from Havenir/version-13-hotfix
fix: KSA VAT report multi currency amount issue
2022-09-04 13:12:21 +05:30
ruthra kumar
1870dbf9a8 Merge pull request #32059 from frappe/mergify/bp/version-13-hotfix/pr-32054
fix: type error on cancellation of Process Deferred Accounting (backport #32054)
2022-09-02 17:10:01 +05:30
ruthra kumar
69a9724422 test: pda document submission and cancellation
(cherry picked from commit 1c385541fa)
2022-09-02 11:15:13 +00:00
ruthra kumar
814dd36a3e fix: incorrect import parameter for cancel PDA
(cherry picked from commit 08f2e4edc3)
2022-09-02 11:15:12 +00:00
ruthra kumar
17ad96998e Merge pull request #32057 from frappe/mergify/bp/version-13-hotfix/pr-32052
fix: key error on consolidated financial report (backport #32052)
2022-09-02 16:41:56 +05:30
ruthra kumar
20919c8626 fix: key error on consolidated financial report
accounts with same name but different account number will throw key
error on consolidated report

(cherry picked from commit 6e8395cccd)
2022-09-02 10:46:23 +00:00
hamzaali15
56d8962e40 fix: KSA VAT report multi currency amount issue
In KSA VAT report amount is not showing correctly for multi currencies because net_amount field is fetched instead of base_net_amount
2022-09-01 15:02:21 +05:00
Deepesh Garg
0a3ac82232 Merge pull request #32031 from frappe/mergify/bp/version-13-hotfix/pr-32030
fix: Loan Interest accruals for 0 rated loans (backport #32030)
2022-08-31 21:04:27 +05:30
Deepesh Garg
b736df3f0b Merge pull request #32036 from niyazrazak/patch-5
fix: lost quotation not to expired
2022-08-31 21:03:31 +05:30
MOHAMMED NIYAS
ea995de4a3 fix: lost quotation not to expired
code is merged in version-14 and conflict in version 13
2022-08-31 15:31:07 +05:30
Deepesh Garg
b7c94e38a6 chore: Add check for principal amount
(cherry picked from commit a76d3827ec)
2022-08-30 15:46:39 +00:00
Deepesh Garg
a3698524ca fix: Loan Interest accruals for 0 rated loans
(cherry picked from commit eefc9b7172)
2022-08-30 15:46:38 +00:00
mergify[bot]
0f1f67dcae fix: force delete old report docs (backport #32026) (#32028)
fix: force delete old report docs (#32026)

(cherry picked from commit ffa3071d36)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-08-30 15:45:47 +05:30
Deepesh Garg
570729b73e Merge pull request #32024 from frappe/mergify/bp/version-13-hotfix/pr-31838
fix(minor): don't print tax rate if its '0' (backport #31838)
2022-08-30 13:32:16 +05:30
ruthra kumar
a46dca57cb fix(minor): don't print tax rate if its '0' (#31838)
(cherry picked from commit 3b4c0a3fc0)
2022-08-30 07:34:26 +00:00
Deepesh Garg
821c1cddfc Merge pull request #32011 from deepeshgarg007/loan_write_off_salary_slip
fix: Loan Write-off for term loans
2022-08-30 11:36:09 +05:30
Deepesh Garg
8217c6dd9f chore: Linting issues and test case fixes 2022-08-30 10:24:31 +05:30
Deepesh Garg
fc030a7de9 Merge pull request #32018 from frappe/mergify/bp/version-13-hotfix/pr-32016
fix: permissions for Task Type (backport #32016)
2022-08-30 08:38:25 +05:30
Raffael Meyer
1157bf887f fix: permissions for Task Type (#32016)
(cherry picked from commit 73f4d5931d)
2022-08-29 16:27:24 +00:00
Deepesh Garg
e64e812679 fix: Loan Write-off for term loans 2022-08-29 18:33:40 +05:30
Deepesh Garg
6025df97ef Merge pull request #31931 from frappe/mergify/bp/version-13-hotfix/pr-31910
fix: Cash and non trade discount calculation (backport #31910)
2022-08-29 15:06:00 +05:30
Deepesh Garg
4793adfefd chore: rounded total for cash and non trade discounts 2022-08-29 14:02:18 +05:30
Rucha Mahabal
7686c9e450 fix: filter leave types and render leave application dashboard w/o from date (#32001) 2022-08-29 12:26:05 +05:30
Rucha Mahabal
074d484d3c fix: do not clear promotion/transfer details if doc is amended (#32000) 2022-08-29 12:10:15 +05:30
Deepesh Garg
332e86af7e Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31910 2022-08-28 17:32:41 +05:30
Deepesh Garg
8eded437d2 Merge pull request #31971 from frappe/mergify/bp/version-13-hotfix/pr-31955
chore: update french translation (backport #31955)
2022-08-28 11:32:34 +05:30
Deepesh Garg
bef7f6114d Merge pull request #31989 from frappe/mergify/bp/version-13-hotfix/pr-31988
chore: remove precision on discount_percentage of Sales Invoice Item (backport #31988)
2022-08-28 11:32:14 +05:30
Deepesh Garg
4095a3dae2 chore: Resolve conflicts 2022-08-26 18:10:09 +05:30
ruthra kumar
0b40117bfd chore: remove precision on discount_percentage of Sales Invoice Item
(cherry picked from commit c42fef541a)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
2022-08-26 10:34:31 +00:00
ruthra kumar
09de1cba92 Merge pull request #31972 from frappe/mergify/bp/version-13-hotfix/pr-31934
refactor: disable discount accounting on Buying module(PI) (backport #31934)
2022-08-25 17:14:44 +05:30
ruthra kumar
51a40ad2fc test: remove discount accounting tests
(cherry picked from commit 277ef04b60)
2022-08-25 16:05:01 +05:30
mergify[bot]
261405cec1 fix(pos): edge case while closing pos (backport #31748) (#31893) 2022-08-25 14:50:24 +05:30
ruthra kumar
1ac9d7f43e refactor: disable discount accounting on Buying module(PI)
(cherry picked from commit a956e20f29)
2022-08-25 14:44:54 +05:30
Florian HENRY
69b3145be4 chore: update fr translation
(cherry picked from commit 299da5d596)
2022-08-25 07:37:26 +00:00
Florian HENRY
5e1d367d7a chore: update fr translation
(cherry picked from commit 1f6f2747d4)
2022-08-25 07:37:25 +00:00
Florian HENRY
10f0baf6ad chore: update french translation
(cherry picked from commit 264f98af14)
2022-08-25 07:37:25 +00:00
Deepesh Garg
20694eb509 Merge pull request #31965 from frappe/mergify/bp/version-13-hotfix/pr-31909
fix: Add docstatus filter for voucher_no in Repost Item Valuation (backport #31909)
2022-08-25 11:25:14 +05:30
HENRY Florian
c2b8d1bd9a feat: In BOM, Operation time can be fix (backport #27063) (#31923)
* feat: In BOM Operation time can be fix (backport #27063)

* chore: only changes necessary for feature

* chore: add test

* chore: linter

* chore: linter

* chore: linter

* chore: fix date in json

* chore: fix test order

* chore: revert test from version-13 not develop

* chore: make test ok
2022-08-25 11:14:35 +05:30
rohitwaghchaure
49bf05c1c2 Merge pull request #31962 from frappe/mergify/bp/version-13-hotfix/pr-31951
fix: Purposes not set in Maintenance Visit (backport #31951)
2022-08-25 10:51:12 +05:30
Sagar Sharma
2cf2885470 fix: Add docstatus filter for voucher_no in Repost Item Valuation
(cherry picked from commit 520306dc87)
2022-08-25 05:20:46 +00:00
Rohit Waghchaure
edfaf99388 fix: Purposes not set
(cherry picked from commit f9a7b31b5b)
2022-08-25 05:19:36 +00:00
Deepesh Garg
3a7a53ab72 Merge pull request #31919 from SolufyPrivateLimited/Solufy-pur_inv_docstatus
fix: Set the condition to create a purchase receipt
2022-08-25 10:35:14 +05:30
mergify[bot]
34537c9dc2 fix: Route condition set for stock ledger (backport #31935) (#31946)
fix: Route condition set for stock ledger (#31935)

(cherry picked from commit 0e26df331c)

Co-authored-by: Solufyin <34390782+Solufyin@users.noreply.github.com>
2022-08-24 13:30:49 +05:30
mergify[bot]
154adcbb58 chore: add Work Order test dependencies (backport #31936) (#31938)
chore: add Work Order test dependencies (#31936)

(cherry picked from commit fe73d55f70)

Co-authored-by: HENRY Florian <florian.henry@open-concept.pro>
2022-08-23 16:10:27 +05:30
Deepesh Garg
6a9d13ff28 chore: Linting issues
(cherry picked from commit 1cb7ae16ab)
2022-08-23 04:50:19 +00:00
Deepesh Garg
7c85b487cd fix: Test cases
(cherry picked from commit ae3dce0cbd)
2022-08-23 04:50:18 +00:00
Deepesh Garg
20e9599fc1 fix: Cash and non trade discount calculation
(cherry picked from commit 3b15966cc9)
2022-08-23 04:50:17 +00:00
Solufyin
b4c992dd4d fix: Set the condition to create a purchase receipt 2022-08-22 12:00:21 +05:30
Suraj Shetty
d46ce05dcd Merge pull request #31903 from frappe/exotel_fixes_v13 2022-08-22 10:52:18 +05:30
Deepesh Garg
6bc6681f92 Merge pull request #31825 from frappe/mergify/bp/version-13-hotfix/pr-31817
fix: limit pos recent order page result (backport #31817)
2022-08-22 10:32:30 +05:30
Deepesh Garg
75d57c1f77 Merge pull request #31889 from frappe/mergify/bp/version-13-hotfix/pr-31871
fix: incorrect buying amount in Gross Profit rpt (backport #31871)
2022-08-22 10:29:47 +05:30
Deepesh Garg
fc7285cc85 Merge pull request #31826 from frappe/mergify/bp/version-13-hotfix/pr-31799
fix: process loan interest accrual (backport #31799)
2022-08-22 10:28:04 +05:30
Suraj Shetty
23a441252b fix: Replace walrus operator 2022-08-22 09:49:15 +05:30
Deepesh Garg
474f34c04c Merge pull request #31868 from frappe/mergify/bp/version-13-hotfix/pr-31856
fix: incorrect tax amt due to different exchange rate in PR and PI (backport #31856)
2022-08-22 09:09:53 +05:30
Deepesh Garg
6606028062 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31799 2022-08-22 09:00:14 +05:30
Deepesh Garg
f58427bffe Merge pull request #31894 from maharshivpatel/fix-tax-withholding-advance-vouchers
fix: TDS calculation for advance payment
2022-08-22 08:58:59 +05:30
Suraj Shetty
89f889068e style: Fix formatting 2022-08-20 14:19:15 +05:30
Suraj Shetty
9263d5ff4d fix: Update received_by if "to" is changed 2022-08-20 14:15:54 +05:30
Suraj Shetty
4df197a282 test: Do not overwrite frappe.form_dict to retain proxy reference
Set frappe.local.form_dict instead
2022-08-20 14:14:16 +05:30
Suraj Shetty
b495969eb8 test: Clean up form_dict
To avoid failures like
https://github.com/frappe/erpnext/runs/5887687369?check_suite_focus=true#step:12:783
2022-08-20 14:14:10 +05:30
Suraj Shetty
5f4760985c test: Fix used frappe._dict to avoid AttributeError down the line
ref: https://github.com/frappe/erpnext/runs/5816574721?check_suite_focus=true#step:12:880
2022-08-20 14:14:03 +05:30
Suraj Shetty
4a61840e39 test: Use FrappeAPITestCase to track coverage 2022-08-20 14:13:56 +05:30
Suraj Shetty
ca48b9de54 test: Fix erroneous code 2022-08-20 14:13:50 +05:30
Suraj Shetty
baab96d02c fix: Handle exception where no employee is returned 2022-08-20 14:13:40 +05:30
Suraj Shetty
f5b967a947 chore: Remove unused code
- and simplify get_call_log
2022-08-20 14:13:24 +05:30
Suraj Shetty
06b1bcbf59 test: Refactor exotel test setup
- Remove unnecessary code
- Move test data to separate file
- Make proper test assertions
2022-08-20 14:12:17 +05:30
Subin Tom
aa8a063103 fix: added tests 2022-08-20 14:11:48 +05:30
Subin Tom
3f46b2a0ce fix: used get_employees_with_number, strip_number methods 2022-08-20 14:10:18 +05:30
Subin Tom
b52962751c fix: linter, sider fixes 2022-08-20 14:07:17 +05:30
Subin Tom
b94154f00a fix: call type doctype, fixes 2022-08-20 14:06:52 +05:30
Subin Tom
90ed14a055 fix: sider fixes 2022-08-20 14:05:49 +05:30
Subin Tom
b65bce8f98 fix: added type of call select field, additional status for agent rejecting call 2022-08-20 14:05:27 +05:30
Subin Tom
66578a58cf fix: added field to show called group, user_id 2022-08-20 14:04:01 +05:30
Subin Tom
476ded2972 fix: added employee name to call log 2022-08-20 14:03:40 +05:30
Subin Tom
ffb1196516 fix: call status fix 2022-08-20 14:03:14 +05:30
Maharshi Patel
a452143782 fix: TDS calculation for advance payment
"against_voucher": ["is", "not set"] was used in query due to which if TDS was added on "advance" payment vouchers and then reconciled against purchase invoice. it will not find those vouchers and consider this as first-time threshold due to which it will calculate Tax for all transactions.
2022-08-19 12:30:47 +05:30
ruthra kumar
6405a9378d fix: incorrect buying amount in Gross Profit rpt
(cherry picked from commit 967dd398e7)
2022-08-18 13:28:45 +00:00
Rucha Mahabal
2471ca58ee Merge pull request #31884 from ruchamahabal/fix-promotion 2022-08-18 12:29:31 +05:30
Rucha Mahabal
a4828407d0 fix: interview rescheduling not working 2022-08-18 12:02:32 +05:30
Rucha Mahabal
36130c6292 fix(minor): save the employee doc on promotion/transfer update 2022-08-18 11:42:29 +05:30
mergify[bot]
4775c42593 fix: Make expense account editable in Purchase Receipt Item (backport #31730) (#31879)
* fix: Make expense account editable in Purchase Receipt Item (#31730)

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

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

* chore: conflicts

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-18 11:15:26 +05:30
mergify[bot]
b0917aaf8a fix: Transit filter for Default Target Warehouse in SE (backport #31839) (#31874)
fix: Transit filter for Default Target Warehouse in SE (#31839)

(cherry picked from commit f1a612245c)

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-17 17:25:31 +05:30
rohitwaghchaure
d349a337e1 Merge pull request #31866 from frappe/mergify/bp/version-13-hotfix/pr-31863
fix: not able to issue expired batches (backport #31863)
2022-08-17 15:43:22 +05:30
ruthra kumar
ba5ee1ca96 fix: incorrect tax amt due to different exchange rate in PR and PI
(cherry picked from commit 5fd0770372)
2022-08-17 09:40:36 +00:00
rohitwaghchaure
41b8563b7a fix: conflicts 2022-08-17 14:47:47 +05:30
Rohit Waghchaure
7ee75ff762 fix: not able to issue expired batches
(cherry picked from commit 795c94384a)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/test_stock_entry.py
2022-08-17 09:16:37 +00:00
mergify[bot]
3274e7681d fix: incorrect rate in BOM exploded items (backport #31513) (#31865)
* fix: incorrect rate in BOM exploded items (#31513)

(cherry picked from commit 313625c349)

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

* chore: conflicts

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-17 14:39:15 +05:30
mergify[bot]
7ddb332f47 fix: incorrect produced-qty in production-plan-item (backport #31706) (#31862)
fix: incorrect produced-qty in production-plan-item (#31706)

(cherry picked from commit 538cd6fdcf)

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-17 13:49:51 +05:30
mergify[bot]
2a615af00a fix: contact search in request for quotation (backport #31828) (#31841)
* fix: contact search in request for quotation (#31828)

(cherry picked from commit e5e88bb9f1)

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

* chore: conflicts

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-17 11:23:24 +05:30
mergify[bot]
ba8dc8925f fix: check item_code in all rows of po_items (backport #31741) (#31843)
fix: check item_code in all rows of po_items (#31741)

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

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2022-08-17 11:21:47 +05:30
Sagar Vora
14891ccf18 Merge pull request #31792 from frappe/mergify/bp/version-13-hotfix/pr-31733
fix: set `billing_address` for purchases in `get_party_details` (backport #31733)
2022-08-16 05:48:48 +00:00
Sagar Vora
15654aae8b fix: use old style for _dict.update 2022-08-13 08:00:27 +00:00
Sagar Vora
d1de4b027c fix: set company_address for purchases in party.js
(cherry picked from commit d05082987f)
2022-08-13 08:00:27 +00:00
Sagar Vora
a7d66fa352 fix: set billing_address for purchases in get_party_details
(cherry picked from commit a3625b3817)
2022-08-13 08:00:27 +00:00
Abhinav Raut
8dea238d12 fix: term loan interest calculation
(cherry picked from commit 534d7ce64b)
2022-08-11 10:18:04 +00:00
Abhinav Raut
acbed434c2 fix: process loan interest accrual
(cherry picked from commit 9ef8d5c5c3)
2022-08-11 10:18:04 +00:00
ruthra kumar
9751f1060e fix: limit pos recent order page result
(cherry picked from commit bb40e38451)
2022-08-11 08:40:05 +00:00
Deepesh Garg
0569f8a857 Merge pull request #31736 from maharshivpatel/fix-india-hsn-report
fix: (india) HSN wise report
2022-08-09 18:58:25 +05:30
Rucha Mahabal
63715bf229 Merge pull request #31813 from ruchamahabal/fix-claim 2022-08-09 18:52:22 +05:30
Rucha Mahabal
63cd4349a6 fix: consider precision while validating advance amount against sanctioned amount 2022-08-09 18:28:19 +05:30
Maharshi Patel
42b395916d fix: f-string and where clause
Used f-string formatting and added conditions to WHERE clause
2022-08-09 18:22:05 +05:30
Rucha Mahabal
0057c10a7d fix: update To Date in Employee Work History (#31811) 2022-08-09 18:19:09 +05:30
Deepesh Garg
3c8c5d01fb Merge pull request #31797 from frappe/mergify/bp/version-13-hotfix/pr-31779
Bug add accouting dimension in asset repair (backport #31779)
2022-08-09 18:18:42 +05:30
Rucha Mahabal
d820757359 fix: Payment Entry button is visible even when claim is fully paid 2022-08-09 18:15:41 +05:30
mergify[bot]
14e59c86aa Tds report (backport #31801) (#31808)
* fix: TDS Computation Summary Report not loading, too many values to unpack
2022-08-09 18:11:13 +05:30
Deepesh Garg
9e16f4e412 chore: resolve conflicts 2022-08-09 17:55:02 +05:30
rohitwaghchaure
6d269a4d89 Merge pull request #31805 from frappe/mergify/bp/version-13-hotfix/pr-31804
fix: incorrect incoming rate set for inter transfer purchase receipt (backport #31804)
2022-08-09 16:26:34 +05:30
Rohit Waghchaure
4e8b39ab3f fix: incorrect incoming rate set for inter transfer purchase receipt
(cherry picked from commit ddd24ea8c8)
2022-08-09 10:31:14 +00:00
Rucha Mahabal
9925eb9982 Merge pull request #31802 from ruchamahabal/fix-salary-slip-tds-v13 2022-08-09 15:33:54 +05:30
Rucha Mahabal
301d199ece test: default amount in slip 2022-08-09 14:57:10 +05:30
Maharshi Patel
6c574fbf33 fix: taxable_value and gst_account_heads
used taxable_value instead of base_net_amount and only appended required GST accounts
2022-08-09 14:39:13 +05:30
Rucha Mahabal
80981d025f fix: incorrect tax calculation in case of reduced payment days 2022-08-09 12:40:06 +05:30
ruthra kumar
a420d0242c chore: patch for creating existing dimensions in asset repair
(cherry picked from commit 80f508c4b1)

# Conflicts:
#	erpnext/patches.txt
2022-08-08 11:09:55 +00:00
ruthra kumar
809d5caf80 fix: add asset repair to accounting dimension list
(cherry picked from commit 452584c4bd)
2022-08-08 11:09:53 +00:00
Deepesh Garg
6f442d14cf Merge pull request #31795 from frappe/mergify/bp/version-13-hotfix/pr-31777
fix: intercompany SO created from Purchase Order throws exception (backport #31777)
2022-08-08 16:37:39 +05:30
ruthra kumar
a17acfdaa2 fix: intercompany SO throws exception
(cherry picked from commit af0a353b79)
2022-08-08 10:37:25 +00:00
Deepesh Garg
3673dea03b Merge pull request #31783 from frappe/mergify/bp/version-13-hotfix/pr-31780
Fix: Loan pending principal amount  (backport #31780)
2022-08-08 14:48:06 +05:30
Deepesh Garg
fb3725752f chore: resolve conflicts 2022-08-08 11:53:49 +05:30
Abhinav Raut
16c94d292c fix: pending principal- amount
(cherry picked from commit a272d73dd9)

# Conflicts:
#	erpnext/loan_management/doctype/loan_repayment/loan_repayment.json
2022-08-05 10:35:05 +00:00
mergify[bot]
0e46b33ee3 fix(ecommerce): remove query to non-existing field (backport #31771) (#31774)
fix(ecommerce): remove query to non-existing field (#31771)

(cherry picked from commit 17b9bfd249)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-08-03 17:08:46 +05:30
mergify[bot]
df716fbd0c ci: fix automated release regex (backport #31770) (#31772)
ci: fix automated release regex (#31770)

[skip ci]

(cherry picked from commit 2defb89962)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-08-03 16:37:39 +05:30
mergify[bot]
0dbfb1589e fix: specify allowed doctype in queries (#31765)
Co-authored-by: Sagar Vora <sagar@resilient.tech>
2022-08-03 12:03:53 +05:30
mergify[bot]
473a43b6b1 fix: getting error to show sales invoice group or print rep… (backport #31756) (#31768)
fix: getting error to show sales invoice group or print rep… (#31756)

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

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

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

i fixed both errors and all working perfectly.

(cherry picked from commit ea88451875)

Co-authored-by: HarryPaulo <paulo_fabris@hotmail.com>
2022-08-03 11:23:10 +05:30
rohitwaghchaure
1a3b3b96c2 Merge pull request #31751 from frappe/mergify/bp/version-13-hotfix/pr-31750
fix: minor URL link (backport #31750)
2022-08-01 14:34:12 +05:30
Rohit Waghchaure
e9e53a74c9 fix: minor changed link
(cherry picked from commit 0e7c4314b4)
2022-08-01 09:02:07 +00:00
Rucha Mahabal
26aef4fb1c fix: statistical component showing up in salary slip (#31746)
* fix: statistical component showing up in salary slip

* fix(test): payment days effect on timesheet salary slio
2022-08-01 12:54:25 +05:30
mergify[bot]
b145fe3b3e fix(pos): validate product bundles while submitting pos invoice (backport #31615) (#31657) 2022-07-31 19:12:14 +05:30
Marica
d83c869d73 Merge pull request #31717 from frappe/mergify/bp/version-13-hotfix/pr-31601
perf: reduce db calls for warehouse wise stock value chart (backport #31601)
2022-07-31 18:26:27 +05:30
Maharshi Patel
08c69c7a76 fix: (india) HSN wise report
Problem:
The previous approach for calculating tax_rate was incorrect.
```
['tax_rate'] * ['number of unique rows']
```
Joining `tabSales Taxes and Charges` was adding unnecessary rows & complexity.

Solution:
Instead of trying to get tax_rate from the main query itself, I used the get_tax_accounts's data to calculate the correct tax_rate.

Todo:
Union Territory
2022-07-29 14:22:31 +05:30
Deepesh Garg
25da4d28ff Merge pull request #31727 from frappe/mergify/bp/version-13-hotfix/pr-31656
fix: use current pos profile on sales return (backport #31656)
2022-07-28 16:32:36 +05:30
Deepesh Garg
f57d2fadbc Merge pull request #31728 from frappe/mergify/bp/version-13-hotfix/pr-31676
fix: enable tax withholding checkbox in PI with supplier_tds (backport #31676)
2022-07-28 16:32:17 +05:30
Deepesh Garg
9e7edd677a Merge pull request #31726 from frappe/mergify/bp/version-13-hotfix/pr-31576
fix: credite note for returned delivery note updates SO's billed percentage (backport #31576)
2022-07-28 16:32:05 +05:30
ruthra kumar
bc7494278d fix: enable tax withholding checkbox in PI with supplier_tds
(cherry picked from commit b461724416)
2022-07-28 10:42:58 +00:00
ruthra kumar
c442b4aef1 fix: use current pos profile on sales return
(cherry picked from commit 5b85af5f1a)
2022-07-28 10:36:23 +00:00
ruthra kumar
ef9b25cf08 test: SO percentage billed when cr_note made against delivery return
(cherry picked from commit 243f66fcd3)
2022-07-28 10:21:19 +00:00
ruthra kumar
2f2d3de306 fix: update SO's percentage billed on credit note
Credit Note created from Sales Return will update precentage billed in
Sales Order accordingly

(cherry picked from commit 04c1019242)
2022-07-28 10:21:18 +00:00
Rucha Mahabal
13639682ce Merge pull request #31724 from ruchamahabal/expense-claim-adv 2022-07-28 14:51:15 +05:30
Rucha Mahabal
c01adae8a5 test: advance amount allocation against expense claim with taxes 2022-07-28 14:27:09 +05:30
Deepesh Garg
8ae691d9dc Merge branch 'version-13' into version-13-hotfix 2022-07-28 13:59:25 +05:30
Alaa Alsalehi
a95c011a93 fix: Reload loan Table in Salary Slip when change Employee (#31525)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
Co-authored-by: bahaaabed <bahaa.9999.sh@gmail.com>
Co-authored-by: newhr1 <104132586+newhr1@users.noreply.github.com>
2022-07-28 13:22:07 +05:30
Rucha Mahabal
42f3592e95 fix: Allow allocating advance amount against Expense Claim taxes 2022-07-28 13:21:29 +05:30
Syed Mujeer Hashmi
0602848caa fix: The Fee details are not fetched in Program Enrollment (#31153) 2022-07-28 12:30:39 +05:30
Rucha Mahabal
9985d28571 chore: Add HR & Payroll deprecation warning (#31720) 2022-07-28 11:43:49 +05:30
Marica
31824c2280 Merge pull request #31716 from frappe/mergify/bp/version-13-hotfix/pr-31687
fix: update fr translations (backport #31687)
2022-07-27 18:01:30 +05:30
Devin Slauenwhite
18d93f8398 fix: sum stock_value and group by warehouse
(cherry picked from commit 1e20358c28)
2022-07-27 12:18:39 +00:00
Devin Slauenwhite
c9443123f9 chore: remove unused import
(cherry picked from commit bc3023318e)
2022-07-27 12:18:38 +00:00
Devin Slauenwhite
0fdec8fac8 pref: reduce count of db calls from n to 2
(cherry picked from commit 73ade04dcf)
2022-07-27 12:18:38 +00:00
marination
6fbb06a878 chore: Merge Conflict 2022-07-27 17:30:46 +05:30
HENRY Florian
89348c1bb4 fix: update fr translations (#31687)
* fix: update fr translations

* fix: update fr translation

* fix: update fr translation

* chore: Replace apostrophe encoding by symbol

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

# Conflicts:
#	erpnext/translations/fr.csv
2022-07-27 11:52:03 +00:00
Marica
598dbc93ac Merge pull request #31669 from frappe/mergify/bp/version-13-hotfix/pr-31579
fix: display customer name on picking list (backport #31579)
2022-07-27 16:45:07 +05:30
Marica
51a4ef3069 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31579 2022-07-27 16:23:22 +05:30
Marica
49fb177f86 chore: Merge Conflicts 2022-07-27 16:22:17 +05:30
Marica
b0aae8a297 Merge pull request #31713 from frappe/mergify/bp/version-13-hotfix/pr-31469
chore: Make `image` field obsolete in Website Item (redundant) (backport #31469)
2022-07-27 16:12:32 +05:30
Marica
e310347ca4 chore: Merge Conflicts 2022-07-27 15:51:01 +05:30
marination
9b1544aa14 fix: Map Item image to Website Item website_image only if published via UI (v13)
- For v12 Items, `website_image` should be mapped from `Item` to `Website Item`

(cherry picked from commit af38baeb3b)

# Conflicts:
#	erpnext/e_commerce/doctype/website_item/website_item.py
2022-07-27 10:14:20 +00:00
marination
a8da5f4566 chore: Remove image use in website item list
(cherry picked from commit 7a6ee8cf2d)
2022-07-27 10:14:18 +00:00
marination
0e469f6d95 chore: Remove image from ProductQuery fields
(cherry picked from commit eec07833f4)
2022-07-27 10:14:18 +00:00
marination
b393c230bd chore: Make image field obsolete in Website Item (redundant)
- Delete Image field and set `website_image` as form's image field for uploads
- Remove instances of `image` field access via Website Item
- Item -> Web Item via Desk: Map Item's `image` to Web Item's `website_image`
- Item -> Web Item via patch: `website_image` will be mapped with thumbnail
- Remove magic that auto-sets `website_image` from `image` in Website Item

(cherry picked from commit 9541354ec7)

# Conflicts:
#	erpnext/e_commerce/doctype/website_item/website_item.py
2022-07-27 10:14:16 +00:00
Rucha Mahabal
32c1bb61de fix: manually generated salary slips overwritten by structure amount (#31711) 2022-07-27 12:44:26 +05:30
MohsinAli
4f023757de fix: payment entry to student (#31708)
Payment entry to student via Payment References(From Journal Entry)
2022-07-27 11:50:15 +05:30
Deepesh Garg
d2d25e17bb Merge pull request #31698 from maharshivpatel/fix-einvoice-margin-internal-transfer
fix: (india) (e-invoice) margin & internal company transfer
2022-07-26 21:59:44 +05:30
Maharshi Patel
a843e784e6 fix: discount and test added
only report net rate if discount is less than 0 and added test.
2022-07-26 17:40:59 +05:30
Rucha Mahabal
8a6432ec3f fix: hero image not loading in portal homepage (#31699) 2022-07-26 13:43:36 +05:30
Maharshi Patel
b97d30aad0 fix: (india) (e-invoice) margin & internal company transfer
When the item price is more than the price list rate ( margin added ) discount value becomes negative. The previous attempt to solve this was to convert discount to absolute value. However, that gives incorrect unit price and discount value. To solve this, I have made changes to report net rates in cases where the margin is added or is an internal company transfer.
2022-07-26 12:57:47 +05:30
Rucha Mahabal
bc7cfe6919 fix: do not update component amount for timesheet components (#31696)
* fix: do not update component amount for timesheet components

* fix: warn the user about overwriting timesheet component amount
2022-07-26 12:35:50 +05:30
Deepesh Garg
85802b0f97 Merge pull request #31602 from rtdany10/patch-19
fix(india): e-way bill json for unregistered gst category
2022-07-22 13:11:19 +05:30
Deepesh Garg
dc67d39ce6 Merge pull request #31665 from maharshivpatel/fix-add-overseas-hsn-report
fix: (india) add entries from overseas invoices in HSN wise report
2022-07-22 13:06:30 +05:30
mergify[bot]
529a47bc88 fix: rounding errors while closing pos (backport #31654) (#31658) 2022-07-22 11:17:59 +05:30
Devin Slauenwhite
2381b81aac fix: make customer_name field read only.
(cherry picked from commit 7083b3148b)

# Conflicts:
#	erpnext/stock/doctype/pick_list/pick_list.json
2022-07-21 13:08:41 +00:00
Devin Slauenwhite
dbf245c687 fix: display customer name on picking list
(cherry picked from commit 0a633a212d)

# Conflicts:
#	erpnext/stock/doctype/pick_list/pick_list.json
2022-07-21 13:08:40 +00:00
Nihantra C. Patel
5cdc267aee fix: Route condition set with proper filter (#31556)
* fix: Redirect to report with proper filter

* Update member.js

* fix: Route condition set with proper filter

* fix: Route condition set with proper filter

* fix: Route condition set with proper filter

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2022-07-21 18:29:06 +05:30
mergify[bot]
95e1021caf fix: update fr translations (backport #31526) (#31666)
fix: update fr translations (#31526)

* fix: update fr translations

* fix: update fr translations

* fix: update fr translations

* fix: update fr translations

* chore: Update french translation

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

Co-authored-by: HENRY Florian <florian.henry@open-concept.pro>
2022-07-21 18:11:32 +05:30
mergify[bot]
7d50f8798a Update de.csv (backport #31596) (#31667)
Update de.csv (#31596)

IN,IM to IN,EIN

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

Co-authored-by: billy995 <48571007+billy995@users.noreply.github.com>
2022-07-21 18:10:46 +05:30
Marica
dd751c6e92 Merge pull request #31664 from frappe/mergify/bp/version-13-hotfix/pr-31544
FIX: quality inspection quick creation from purchase receipt: pre-fill sample size if available (backport #31544)
2022-07-21 14:13:48 +05:30
Frappe PR Bot
69087b8790 chore(release): Bumped to Version 13.36.1
## [13.36.1](https://github.com/frappe/erpnext/compare/v13.36.0...v13.36.1) (2022-07-21)

### Bug Fixes

* ensure defaults removed in bad frappe patch get set again (backport [#31659](https://github.com/frappe/erpnext/issues/31659)) ([2ae3bd9](2ae3bd9fed))
2022-07-21 08:26:20 +00:00
mergify[bot]
763787b0a5 fix: ensure defaults removed in bad frappe patch get set again (backport #31659) (#31661)
fix: ensure defaults removed in bad frappe patch get set again (#31659)

(cherry picked from commit bf2833b8ee)

Co-authored-by: Sagar Vora <sagar@resilient.tech>
2022-07-21 13:54:55 +05:30
mergify[bot]
2ae3bd9fed fix: ensure defaults removed in bad frappe patch get set again (backport #31659)
fix: ensure defaults removed in bad frappe patch get set again (#31659)

(cherry picked from commit bf2833b8ee)
(cherry picked from commit 9071018e4e)

Co-authored-by: Sagar Vora <sagar@resilient.tech>
2022-07-21 13:54:40 +05:30
Marc de Lima Lucio
1998223ee3 FIX: quality inspection quick creation from purchase receipt: pre-fill sample size if available (#31544)
FIX: quality inspection quick creation from transaction documents: pre-fill sample size if available

Co-authored-by: Marica <maricadsouza221197@gmail.com>
(cherry picked from commit 048c037842)
2022-07-21 08:17:48 +00:00
mergify[bot]
794fd0819f fix: correct Brazilian portuguese translations (backport #31498) (#31660)
fix: correct Brazilian portuguese translations (#31498)

* fix brazilian portuguese translations

* minor adjustments

* fix minor adjustments

* fix: remove legacy pt_br.csv

* chore: Fix translation quotation marks

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

Co-authored-by: Marco Fonseca <62901164+treasuryesc@users.noreply.github.com>
2022-07-21 13:47:18 +05:30
Maharshi Patel
1d69ce1932 fix: (india) add overseas in HSN wise report
SQL query had WHERE `tabSales Taxes and Charges`.parent = `tabSales Invoice`.name. However, In overseas, there is no `tabSales Taxes and Charges` so I changed the query to use a combination of INNER JOIN and LEFT JOIN to fetch all entries.
2022-07-21 13:27:30 +05:30
Maharshi Patel
8e23c6ad69 fix: assign duplicate_items_msg outside conditional (#31639)
duplicate_items_msg was defined inside if the statement  of stock_item so when duplicate was found in non_stock_item it raised referenced before assignment
2022-07-20 21:25:36 +05:30
Frappe PR Bot
32ba8c7d6d chore(release): Bumped to Version 13.36.0
# [13.36.0](https://github.com/frappe/erpnext/compare/v13.35.3...v13.36.0) (2022-07-20)

### Bug Fixes

* (india) (e-invoice) allow generation for UIN Holders ([82539fc](82539fc18a))
* (india) (e-invoice) CN / DN with 0 qty ([84f260e](84f260e1d8))
* (india) (e-invoice) discount calculation ([1f4d434](1f4d434675))
* (india) (e-invoice) discount in CN/DN ([6e6b55e](6e6b55ece0))
* (india) (e-invoice) qty should not be changed to 1 ([b0efb98](b0efb98237))
* (india)(einvoice) discount for CN DN ([844758a](844758a27c))
* added Section translation in German for Salary Slip (backport [#31608](https://github.com/frappe/erpnext/issues/31608)) ([#31616](https://github.com/frappe/erpnext/issues/31616)) ([12d3d6a](12d3d6ab49))
* Allow multi currency invoice against single party account ([9ac9c46](9ac9c46dab))
* **India:** Inward supplies from Composition Supplier in 3B report ([b147ce4](b147ce4206))
* LCV updates wrong future qty/Bin qty ([f0ecdbe](f0ecdbef5a))
* linter ([9a3c846](9a3c84663f))
* move auto attendance job to long queue ([8eb9aaa](8eb9aaafe9))
* on cancel for loan refund ([8237d94](8237d94b11))
* Opening Invoice Creation Tool msgprint ([cb80bcd](cb80bcd0ee))
* Patch to make accounting dimension in orders ([55399f2](55399f2e58))
* Remove unnecessary list comprehensions ([711501b](711501be5e))
* Removed 'Allow Monthly Depreciation' checkbox and fixed wdv depreciation rate ([616e3c6](616e3c66b9))
* Renamed status field to gle_processing_status ([1180135](1180135067))
* Set Amount in Supplied Item table ([72e8c02](72e8c02ae0))
* set default_bom for item ([def622c](def622c13e))
* set status on submit/cancel ([04b077a](04b077a89f))
* slow stock reposting ([a5dae92](a5dae9264d))
* Supplier details in TDS monthly report ([#31599](https://github.com/frappe/erpnext/issues/31599)) ([431d79f](431d79f516))
* Tax amount not considered in Expense Claim Journal Entry ([90c751f](90c751f648))
* test ([9631ffd](9631ffd215))

### Features

* add adjustment amount to loan ([d5ce780](d5ce780e87))

### Performance Improvements

* index shift type and employee in checkins and assignment to avoid full table scans ([6938025](6938025952))
* Optimization of gl entry processing logic in period closing voucher ([903e42f](903e42fbdd))
* Replace `db.get_all` with `db.exists` in `is_holiday` ([85c554e](85c554eab5))
* Replace `get_doc` with `db.get_value` in `get_shift_details` ([abb7ac5](abb7ac5a0b))
* Use `get_cached_value` and `get_cached_doc` ([8c9035d](8c9035d914))
2022-07-20 15:21:11 +00:00
Deepesh Garg
4d67695294 Merge pull request #31647 from frappe/version-13-hotfix
chore: weekly version-13 release
2022-07-20 20:49:09 +05:30
Deepesh Garg
50fbfd9839 Merge pull request #31649 from frappe/mergify/bp/version-13-hotfix/pr-31648
fix: Patch to make accounting dimension in orders (backport #31648)
2022-07-20 20:01:27 +05:30
Deepesh Garg
e36c74ac44 chore: resolve conflicts 2022-07-20 19:39:18 +05:30
Deepesh Garg
45e25b2d56 chore: do not re run patch
(cherry picked from commit cad2035e07)

# Conflicts:
#	erpnext/patches.txt
2022-07-20 13:32:41 +00:00
Deepesh Garg
55399f2e58 fix: Patch to make accounting dimension in orders
(cherry picked from commit d46e406db7)

# Conflicts:
#	erpnext/patches.txt
2022-07-20 13:32:41 +00:00
Deepesh Garg
a5cd81c73c Merge branch 'version-13' into version-13-hotfix 2022-07-20 17:52:28 +05:30
Nabin Hait
52c8e0a6ea Merge pull request #31644 from nabinhait/asset-monthly-depreciation-v13-hotfix
fix: Removed 'Allow Monthly Depreciation' checkbox
2022-07-20 17:33:15 +05:30
Nabin Hait
616e3c66b9 fix: Removed 'Allow Monthly Depreciation' checkbox and fixed wdv depreciation rate 2022-07-20 17:32:28 +05:30
Nabin Hait
a6e97ea9ab Merge pull request #31626 from nabinhait/pcv-perf-enhancements
perf: Optimization of gl entry processing logic in period closing voucher
2022-07-20 15:39:39 +05:30
Deepesh Garg
2c5e117f01 Merge pull request #31629 from maharshivpatel/fix-einvoice-discounts
fix: (india) (e-invoice) convert discount to absolute value
2022-07-20 13:18:14 +05:30
Marica
a65dd58444 Merge pull request #31416 from frappe/mergify/bp/version-13-hotfix/pr-31412
fix: set default_bom for item (backport #31412)
2022-07-20 11:31:55 +05:30
Nabin Hait
04b077a89f fix: set status on submit/cancel 2022-07-20 11:10:36 +05:30
Nabin Hait
1180135067 fix: Renamed status field to gle_processing_status 2022-07-20 10:50:44 +05:30
Rucha Mahabal
eae4b06a80 Merge pull request #31630 from ruchamahabal/auto-attendance-perf 2022-07-20 10:25:02 +05:30
Maharshi Patel
b0efb98237 fix: (india) (e-invoice) qty should not be changed to 1
Qty of 0 is allowed so can't change item.qty to 1  instead created item_qty and used it.
2022-07-19 22:29:38 +05:30
rohitwaghchaure
e7e1847ec9 Merge pull request #31634 from frappe/mergify/bp/version-13-hotfix/pr-31631
fix: slow stock reposting (backport #31631)
2022-07-19 19:26:35 +05:30
Rohit Waghchaure
a5dae9264d fix: slow stock reposting
(cherry picked from commit 1d80d37ccf)
2022-07-19 13:18:13 +00:00
Marica
284095106c Merge pull request #31588 from SolufyPrivateLimited/Solufyin-oict
fix: Opening Invoice Creation Tool msgprint
2022-07-19 17:47:44 +05:30
Rucha Mahabal
8eb9aaafe9 fix: move auto attendance job to long queue 2022-07-19 16:07:18 +05:30
Rucha Mahabal
6938025952 perf: index shift type and employee in checkins and assignment to avoid full table scans 2022-07-19 16:02:31 +05:30
Maharshi Patel
6e6b55ece0 fix: (india) (e-invoice) discount in CN/DN
discount fixes
2022-07-19 15:31:34 +05:30
Rucha Mahabal
8c9035d914 perf: Use get_cached_value and get_cached_doc 2022-07-19 14:47:14 +05:30
Nabin Hait
cfb11f4b84 test: Added test for PCV cancellation 2022-07-19 14:31:02 +05:30
Marica
feffc3af27 Merge pull request #31627 from frappe/mergify/bp/version-13-hotfix/pr-31515
fix: LCV updates wrong future qty/Bin qty (backport #31515)
2022-07-19 14:21:33 +05:30
marination
ca9c6e1651 test: LCV impact on future stock balancees
(cherry picked from commit de9ea70ce3)
2022-07-19 08:10:22 +00:00
marination
f0ecdbef5a fix: LCV updates wrong future qty/Bin qty
- As -ve LCV SLE case is returned from `repost_current_voucher`, future qty is not updated
- This just doubly shifts all future qty which is then fixed by a repost
- Until the repost balance values are wrong
- Bin continues to show wrong projected qty even after repost, this is fixed by next SLE that recalculates Bin

(cherry picked from commit 7a5fd71a6c)
2022-07-19 08:10:21 +00:00
Marica
31930a16fa Merge pull request #31624 from marination/sub-contract-amount
fix: Set `amount` in Raw Supplied Items table
2022-07-19 12:55:20 +05:30
Deepesh Garg
4f55fef782 Merge pull request #31623 from frappe/mergify/bp/version-13-hotfix/pr-31599
fix: Supplier details in TDS monthly report (backport #31599)
2022-07-19 12:20:29 +05:30
marination
72e8c02ae0 fix: Set Amount in Supplied Item table 2022-07-19 11:47:25 +05:30
Deepesh Garg
f02596242c Merge pull request #31609 from deepeshgarg007/gstr_3b_gst_category
fix(India): Inward supplies from Composition Supplier in GST 3B report
2022-07-19 10:23:44 +05:30
Deepesh Garg
431d79f516 fix: Supplier details in TDS monthly report (#31599)
(cherry picked from commit a6ff4db2ec)
2022-07-19 04:45:28 +00:00
Rucha Mahabal
711501be5e fix: Remove unnecessary list comprehensions 2022-07-18 21:32:04 +05:30
Rucha Mahabal
abb7ac5a0b perf: Replace get_doc with db.get_value in get_shift_details 2022-07-18 21:13:02 +05:30
Rucha Mahabal
85c554eab5 perf: Replace db.get_all with db.exists in is_holiday
- widely used function call, fetching all rows for holidays
2022-07-18 21:08:15 +05:30
Nabin Hait
903e42fbdd perf: Optimization of gl entry processing logic in period closing voucher 2022-07-18 17:59:42 +05:30
mergify[bot]
12d3d6ab49 fix: added Section translation in German for Salary Slip (backport #31608) (#31616)
Co-authored-by: Wolfram Schmidt <wolfram.schmidt@phamos.eu>
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-07-18 13:41:42 +05:30
Rucha Mahabal
26497f9c0e Merge pull request #31614 from ruchamahabal/fix-claim-payment
fix: Tax amount not considered in Expense Claim Journal Entry
2022-07-18 13:24:12 +05:30
Rucha Mahabal
6a7549d4b2 test: Journal Entry against Expense Claim 2022-07-18 12:57:50 +05:30
Rucha Mahabal
90c751f648 fix: Tax amount not considered in Expense Claim Journal Entry
- add a helper function `get_outstanding_amount_for_claim` for uniformity in usages
2022-07-18 12:39:09 +05:30
Frappe PR Bot
1090862296 chore(release): Bumped to Version 13.35.3
## [13.35.3](https://github.com/frappe/erpnext/compare/v13.35.2...v13.35.3) (2022-07-18)

### Bug Fixes

* (india) (e-invoice) CN / DN with 0 qty and discount issues ([425abcd](425abcdea7))
2022-07-18 05:21:17 +00:00
mergify[bot]
425abcdea7 fix: (india) (e-invoice) CN / DN with 0 qty and discount issues
* fix: (india) (e-invoice) CN / DN with 0 qty
2022-07-18 10:49:41 +05:30
Deepesh Garg
144057f7e3 Merge pull request #31583 from maharshivpatel/fix-e_invoice-cn-dn-qty-0-error
fix: (india) (e-invoice) CN / DN with 0 qty and discount issues
2022-07-18 09:09:19 +05:30
Maharshi Patel
9631ffd215 fix: test
change setting to run test with correct use case.
2022-07-17 15:40:55 +05:30
Maharshi Patel
9a3c84663f fix: linter 2022-07-17 15:22:37 +05:30
Maharshi Patel
2066e5a53a discount settings for e-invoice added and test cases 2022-07-17 15:08:40 +05:30
Deepesh Garg
b147ce4206 fix(India): Inward supplies from Composition Supplier in 3B report 2022-07-17 00:43:25 +05:30
Deepesh Garg
0d6beed546 test: Update test case 2022-07-16 14:06:01 +05:30
Maharshi Patel
844758a27c fix: (india)(einvoice) discount for CN DN 2022-07-15 16:54:10 +05:30
Maharshi Patel
1f4d434675 fix: (india) (e-invoice) discount calculation
I have added fixes for discount in e-inovice.
2022-07-15 16:03:42 +05:30
Dany Robert
01d6df45d0 fix(india): e-way bill json for unregistered gst category 2022-07-15 11:17:41 +05:30
Deepesh Garg
8c7b836de8 Merge pull request #31595 from maharshivpatel/fix-uin-e_invoice
fix: (india) (e-invoice) allow generation for UIN Holders
2022-07-14 21:25:45 +05:30
Deepesh Garg
9fd1fad259 Merge pull request #31572 from frappe/mergify/bp/version-13-hotfix/pr-31566
fix: Allow multi currency invoice against single party account (backport #31566)
2022-07-14 21:25:17 +05:30
Deepesh Garg
dfa0638baf Merge pull request #31577 from frappe/mergify/bp/version-13-hotfix/pr-31543
feat: Loan balance adjustment doctypes (backport #31543)
2022-07-14 21:24:49 +05:30
Deepesh Garg
0a5bc86d1b chore: resolve conflicts 2022-07-14 17:20:32 +05:30
Deepesh Garg
bdd15895ff chore: resolve conflicts 2022-07-14 17:06:01 +05:30
Maharshi Patel
82539fc18a fix: (india) (e-invoice) allow generation for UIN Holders
e-invoice is required for UIN Holders and they should be treated as Registered Regular. there was incorrect if hasattr check that prevented UIN number validation.
2022-07-14 15:12:51 +05:30
Solufyin
cb80bcd0ee fix: Opening Invoice Creation Tool msgprint 2022-07-13 11:55:47 +05:30
Maharshi Patel
84f260e1d8 fix: (india) (e-invoice) CN / DN with 0 qty
Qty 0 is allowed when creating Credit or Debit Note this caused ZeroDivisionError during unit_rate calculation.
fixed the issue by adding required conditionals.
2022-07-13 01:39:36 +05:30
Sagar Sharma
023c5db3bc Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31412 2022-07-12 23:33:48 +05:30
Deepesh Garg
0b3958d692 chore: Linting Issues
(cherry picked from commit 0bac030ca7)
2022-07-12 12:56:46 +00:00
Deepesh Garg
c2a7dc7754 chore: Linting Issues
(cherry picked from commit d933ff5cf6)
2022-07-12 12:56:46 +00:00
Abhinav Raut
8237d94b11 fix: on cancel for loan refund
(cherry picked from commit 13b7ed1e2c)
2022-07-12 12:56:45 +00:00
Labeeb Mattra
0713a240a7 Consider refund_amount in pending principal amount
(cherry picked from commit 35f2717ad2)
2022-07-12 12:56:45 +00:00
Labeeb Mattra
61b2a1c3cb fix excess amount calculation in loan refund
(cherry picked from commit 9df1413adb)
2022-07-12 12:56:44 +00:00
Labeeb Mattra
94be7a95f7 Update adjustment_type field options
(cherry picked from commit 245b0c7818)
2022-07-12 12:56:44 +00:00
Labeeb Mattra
7ba0326bdd fix adjustment amount field name
(cherry picked from commit 6cc09ef3a2)
2022-07-12 12:56:43 +00:00
Labeeb Mattra
86af1ef6ed fix indent and imports
(cherry picked from commit 0ed6382ab6)
2022-07-12 12:56:43 +00:00
Labeeb Mattra
b995338d0c fix lint
(cherry picked from commit 8434ec09c3)
2022-07-12 12:56:43 +00:00
Labeeb Mattra
0856e14c13 Use adjustment amounts in pending principal amnt
(cherry picked from commit 1b5b2138ee)
2022-07-12 12:56:42 +00:00
Labeeb Mattra
ff5ee2bbac Add ref no to balance adjustment remarks
(cherry picked from commit a1a51ce1a6)
2022-07-12 12:56:42 +00:00
Labeeb Mattra
a36e8a83d8 Add reference number to repayment remarks
(cherry picked from commit 74dbf8c5d9)

# Conflicts:
#	erpnext/loan_management/doctype/loan_repayment/loan_repayment.py
2022-07-12 12:56:41 +00:00
Labeeb Mattra
d81e7e2421 Add NPA checkbox in Loan
(cherry picked from commit 27a8e16b28)
2022-07-12 12:56:41 +00:00
Labeeb Mattra
8181d61293 Update list view for Accrual and Shortfall
(cherry picked from commit 7d6e4898c4)
2022-07-12 12:56:40 +00:00
Labeeb Mattra
548aac5b11 Remove loan account field from doctype
(cherry picked from commit 6febcd529b)
2022-07-12 12:56:40 +00:00
Labeeb Mattra
de69fee6aa Use new adjustment amount fields
(cherry picked from commit 7d468a8778)
2022-07-12 12:56:39 +00:00
Labeeb Mattra
c3e6d7ba29 Seperate credit and debit adjust amount fields in Loan
(cherry picked from commit d6f632a770)
2022-07-12 12:56:38 +00:00
Abhinav Raut
d5ce780e87 feat: add adjustment amount to loan
- fix: bugs in loan balance adjustment

(cherry picked from commit 5c0a25012c)
2022-07-12 12:56:38 +00:00
Labeeb Mattra
a2125e694d Add Loan Balance Adjustment doctype
(cherry picked from commit 2e8f056514)
2022-07-12 12:56:37 +00:00
Labeeb Mattra
0d94653c5b Add Loan Refund doctype
(cherry picked from commit e1682965c5)
2022-07-12 12:56:36 +00:00
Labeeb Mattra
d9723a12c4 Add refund amount to loan
(cherry picked from commit 88cd780ca1)
2022-07-12 12:56:36 +00:00
Labeeb Mattra
d24cb01a9d Add more loan interest accrual types
(cherry picked from commit a81da2ea85)
2022-07-12 12:56:35 +00:00
Labeeb Mattra
ab214bcdfc update loan interest accrual types
(cherry picked from commit 900c878e03)
2022-07-12 12:56:35 +00:00
Frappe PR Bot
751fbd6794 chore(release): Bumped to Version 13.35.2
## [13.35.2](https://github.com/frappe/erpnext/compare/v13.35.1...v13.35.2) (2022-07-12)

### Bug Fixes

* components in the same table don't get updated value of prev payment-days based component ([01beb6f](01beb6f391))
* conflicts ([2045df1](2045df19f9))
* Incorrect provisional expense booking while reposting ([d182137](d182137ed1))
* timeout error while reposting ([07b80c2](07b80c295d))
* Use Contact Name instead of Supplier in RFQ Email ([b0e17de](b0e17dea2a))
* Use fallback conversion factor while setting incoming rate for petty purchase ([2a432c2](2a432c22d4))
* Validate payment-days-based dependent component ([a28c7cf](a28c7cf094))
2022-07-12 09:29:26 +00:00
Deepesh Garg
36566c1d14 Merge pull request #31574 from frappe/version-13-hotfix
chore: weekly version-13 release
2022-07-12 14:57:39 +05:30
Rucha Mahabal
28e4e4320e Merge pull request #31521 from ruchamahabal/fix-salary-calc-pd
fix: components in the same table don't get updated value of prev payment-days based component
2022-07-12 12:06:21 +05:30
Rucha Mahabal
1133062bfc Merge branch 'version-13-hotfix' into fix-salary-calc-pd 2022-07-12 11:42:07 +05:30
Rucha Mahabal
a28c7cf094 fix: Validate payment-days-based dependent component 2022-07-12 11:39:00 +05:30
Deepesh Garg
1c686c732e chore: fix query
(cherry picked from commit e04e67c6bf)
2022-07-11 15:44:49 +00:00
Deepesh Garg
66c5290dee chore: Ignore validation
(cherry picked from commit 3cf609fab1)
2022-07-11 15:44:49 +00:00
Deepesh Garg
9ac9c46dab fix: Allow multi currency invoice against single party account
(cherry picked from commit c83fbd5c50)

# Conflicts:
#	erpnext/accounts/doctype/accounts_settings/accounts_settings.json
2022-07-11 15:44:48 +00:00
Sagar Sharma
faa489bc73 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31412 2022-07-11 17:11:21 +05:30
Sagar Sharma
b17850ac3e chore: conflicts 2022-07-11 17:09:30 +05:30
Marica
7d36bdf292 Merge pull request #31565 from frappe/mergify/bp/version-13-hotfix/pr-31550
fix: Use Contact Name instead of Supplier in RFQ Email (backport #31550)
2022-07-11 12:35:18 +05:30
marination
b331c462ef chore: Instantiate variable unconditionally
(cherry picked from commit 300e812a1f)
2022-07-11 05:52:08 +00:00
marination
b0e17dea2a fix: Use Contact Name instead of Supplier in RFQ Email
(cherry picked from commit 88ac519b24)
2022-07-11 05:52:07 +00:00
Frappe PR Bot
be3b010b44 chore(release): Bumped to Version 13.35.1
## [13.35.1](https://github.com/frappe/erpnext/compare/v13.35.0...v13.35.1) (2022-07-08)

### Bug Fixes

* Use fallback conversion factor while setting incoming rate for petty purchase ([bac8546](bac854628f))
2022-07-08 10:20:30 +00:00
Deepesh Garg
45799f51b3 Merge pull request #31558 from frappe/mergify/bp/version-13/pr-31529
fix: Use fallback conversion factor while setting incoming rate for petty purchase (backport #31529)
2022-07-08 15:48:47 +05:30
marination
bac854628f fix: Use fallback conversion factor while setting incoming rate for petty purchase
- PIs for petty items (that do not need an Item record) are allowed using Item Name field
- If a different UOM is used in this case, conversion factor stays 0 and causes an error
- Fallback to 1 in `set_incoming_rate` for buying
- Selling will need a proper item, so this change is not needed there

(cherry picked from commit aa043fe961)
2022-07-08 09:47:05 +00:00
Deepesh Garg
6d04bafb04 Merge pull request #31518 from frappe/mergify/bp/version-13-hotfix/pr-31516
fix: Incorrect provisional expense booking while reposting (backport #31516)
2022-07-07 11:53:58 +05:30
rohitwaghchaure
c4f39c7b8b Merge pull request #31538 from frappe/mergify/bp/version-13-hotfix/pr-31519
fix: timeout error while reposting (backport #31519)
2022-07-06 14:33:26 +05:30
rohitwaghchaure
2045df19f9 fix: conflicts 2022-07-06 12:34:10 +05:30
Rohit Waghchaure
07b80c295d fix: timeout error while reposting
(cherry picked from commit 78c8bb251e)

# Conflicts:
#	erpnext/stock/doctype/stock_entry/test_stock_entry.py
2022-07-06 07:02:31 +00:00
Marica
a8c3882400 Merge pull request #31534 from frappe/mergify/bp/version-13-hotfix/pr-31529
fix: Use fallback conversion factor while setting incoming rate for petty purchase (backport #31529)
2022-07-06 01:20:51 +05:30
marination
2a432c22d4 fix: Use fallback conversion factor while setting incoming rate for petty purchase
- PIs for petty items (that do not need an Item record) are allowed using Item Name field
- If a different UOM is used in this case, conversion factor stays 0 and causes an error
- Fallback to 1 in `set_incoming_rate` for buying
- Selling will need a proper item, so this change is not needed there

(cherry picked from commit aa043fe961)
2022-07-05 19:09:36 +00:00
Deepesh Garg
776e807ade Merge pull request #31530 from vorasmit/warn-remove-india
chore: deprecation warning for remove-india
2022-07-05 22:46:33 +05:30
Smit Vora
6bda2a0865 chore: deprecation warning for remove-india 2022-07-05 21:20:51 +05:30
Frappe PR Bot
5072e6b1dc chore(release): Bumped to Version 13.35.0
# [13.35.0](https://github.com/frappe/erpnext/compare/v13.34.2...v13.35.0) (2022-07-05)

### Bug Fixes

* **India:** Discounts in E-Invoicing ([9ba7290](9ba7290dc9))
* Internal PI link in Sales Invoice ([8727a6c](8727a6c5da))
* Modify opts parameter misspell (backport [#31476](https://github.com/frappe/erpnext/issues/31476)) ([#31477](https://github.com/frappe/erpnext/issues/31477)) ([4d987a9](4d987a9510)), closes [#31474](https://github.com/frappe/erpnext/issues/31474)
* offset some scheduled jobs to avoid locks (backport [#31466](https://github.com/frappe/erpnext/issues/31466)) ([#31489](https://github.com/frappe/erpnext/issues/31489)) ([b1c6d78](b1c6d789a9))
* **UX:** dont apply price list  when changing batch on mapped docs (backport [#31503](https://github.com/frappe/erpnext/issues/31503)) ([#31504](https://github.com/frappe/erpnext/issues/31504)) ([31fd263](31fd263825))

### Features

* Cash and Non trade discounts in Sales Invoice ([fd2ec25](fd2ec25588))
2022-07-05 08:55:20 +00:00
Deepesh Garg
a8180796db Merge pull request #31524 from frappe/version-13-hotfix
chore: weekly version-13 release
2022-07-05 14:22:20 +05:30
Rucha Mahabal
01beb6f391 fix: components in the same table don't get updated value of prev payment-days based component 2022-07-04 23:14:26 +05:30
Deepesh Garg
d182137ed1 fix: Incorrect provisional expense booking while reposting
(cherry picked from commit 60aad31162)
2022-07-04 14:58:41 +00:00
mergify[bot]
31fd263825 fix(UX): dont apply price list when changing batch on mapped docs (backport #31503) (#31504)
fix(UX): dont apply price list  when changing batch on mapped docs (#31503)

fix(UX): dont apply price list batch change on mapped docs

(cherry picked from commit 7e40c86c56)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-07-04 17:02:11 +05:30
Ankush Menat
5fc93076f4 chore: ignore late binding warnings
Most are false positives

[skip ci]
2022-07-04 13:22:01 +05:30
Deepesh Garg
827794b676 Merge pull request #31510 from frappe/mergify/bp/version-13-hotfix/pr-31405
feat: Cash and Non trade discounts in Sales Invoice (backport #31405)
2022-07-04 11:58:45 +05:30
Deepesh Garg
c72ae178fa chore: resolve conflicts 2022-07-03 14:05:34 +05:30
Deepesh Garg
4a5d681c77 chore: use get instead of . operator
(cherry picked from commit e54ec4b9b6)
2022-07-03 08:05:23 +00:00
Deepesh Garg
57dc1026c8 test: Add test for einvoice discounts
(cherry picked from commit 38352b3e46)
2022-07-03 08:05:22 +00:00
Deepesh Garg
9ba7290dc9 fix(India): Discounts in E-Invoicing
(cherry picked from commit f337213f33)
2022-07-03 08:05:21 +00:00
Deepesh Garg
fd2ec25588 feat: Cash and Non trade discounts in Sales Invoice
(cherry picked from commit 169ff5a0dd)

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.js
2022-07-03 08:05:19 +00:00
Deepesh Garg
eb85ef974a Merge pull request #31508 from frappe/mergify/bp/version-13-hotfix/pr-31493
fix: Internal PI link in Sales Invoice (backport #31493)
2022-07-03 10:09:51 +05:30
Deepesh Garg
8727a6c5da fix: Internal PI link in Sales Invoice
(cherry picked from commit 536e768ba9)
2022-07-02 17:17:50 +00:00
Ankush Menat
1b9a4483d4 chore(meta): update CODEOWNERS 2022-07-01 15:44:18 +05:30
Deepesh Garg
fecf567e92 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31412 2022-06-30 16:46:59 +05:30
Ankush Menat
2e130c335a ci: reduce container count on v13 2022-06-30 11:38:12 +05:30
mergify[bot]
b1c6d789a9 fix: offset some scheduled jobs to avoid locks (backport #31466) (#31489)
fix: offset some scheduled jobs to avoid locks (#31466)

If your site has multiple background workers then there's possibility
that two jobs will execute in parallal, this creates problem when both
are on operating on same data.

This PR adds a separate section for hourly and daily jobs which have
frequency offset from default frequency to avoid such conflicts.

(cherry picked from commit 5d73697c64)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-06-30 11:35:01 +05:30
mergify[bot]
4d987a9510 fix: Modify opts parameter misspell (backport #31476) (#31477)
fix: Modify opts parameter misspell (#31476)

Modify opts parameter misspell

closes #31474

(cherry picked from commit 2a619fd789)

Co-authored-by: gn306029 <gn306029@users.noreply.github.com>
2022-06-29 14:58:03 +05:30
Frappe PR Bot
43cb4fcbb1 chore(release): Bumped to Version 13.34.2
## [13.34.2](https://github.com/frappe/erpnext/compare/v13.34.1...v13.34.2) (2022-06-28)

### Bug Fixes

* add UOM validation for planned-qty ([559bde3](559bde3135))
* dont update RM items table if not required (backport [#31408](https://github.com/frappe/erpnext/issues/31408)) ([#31457](https://github.com/frappe/erpnext/issues/31457)) ([8155306](815530650c))
* General Ledger and TB opening entries mismatch issues ([a0c5c73](a0c5c730f5))
* Monthly depreciation using WDV method ([e7659a1](e7659a10e4))
* Quotation and Sales Order item sync ([2219132](2219132fdb))
* Respect system precision for user facing balance qty values ([#30837](https://github.com/frappe/erpnext/issues/30837)) ([642b9c5](642b9c5466))
* **Salary Slip:** Components not updated when amount evaluates to 0 due to payment days ([#31425](https://github.com/frappe/erpnext/issues/31425)) ([abfe926](abfe926a45))
* translation for filter status on report ([736f206](736f20656d))
* update ru translate (backport [#31404](https://github.com/frappe/erpnext/issues/31404)) ([#31417](https://github.com/frappe/erpnext/issues/31417)) ([8b78a12](8b78a122e7))
2022-06-28 07:31:44 +00:00
Deepesh Garg
0d5bb92149 Merge pull request #31464 from frappe/version-13-hotfix
chore: weekly version-13 release
2022-06-28 13:00:06 +05:30
mergify[bot]
815530650c fix: dont update RM items table if not required (backport #31408) (#31457)
fix: dont update RM items table if not required (#31408)

Currently on PO update RM item table is auto computed again and again,
if there was any transfer/consumption against that then it will be lost.

This change:
1. Disables updating RM table if no change in qty of FG was made. Since
   RM table can't possibly be different with same FG qty.
2. Blocks update completely if qty is changed and RM items are already
   transferred.

(cherry picked from commit dd11f26eba)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-06-28 10:28:11 +05:30
Deepesh Garg
8ab65bc44e Merge pull request #31453 from frappe/mergify/bp/version-13-hotfix/pr-31446
fix: Quotation and Sales Order item sync (backport #31446)
2022-06-28 08:49:21 +05:30
mergify[bot]
5e7cad476f refactor: clean up product bundle client side code (backport #31455) (#31456)
refactor: clean up product bundle client side code (#31455)

refactor: clean up product bundle cient side code

- Remove deprecated CUR_FRM scripts
- Remove client side fetches and move it to doctype schema

(cherry picked from commit 20dac08f5f)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-06-27 18:33:17 +05:30
Deepesh Garg
2219132fdb fix: Quotation and Sales Order item sync
(cherry picked from commit 58fe220479)
2022-06-27 05:48:39 +00:00
Deepesh Garg
873d1ecdf0 Merge pull request #31447 from frappe/mergify/bp/version-13-hotfix/pr-31439
fix: General Ledger and TB opening entries mismatch issues (backport #31439)
2022-06-27 09:47:00 +05:30
Deepesh Garg
a0c5c730f5 fix: General Ledger and TB opening entries mismatch issues
(cherry picked from commit 6acd0325be)
2022-06-24 16:15:38 +00:00
Nabin Hait
e7659a10e4 fix: Monthly depreciation using WDV method 2022-06-24 21:12:31 +05:30
Sagar Sharma
96bd493a6f Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31412 2022-06-24 13:17:43 +05:30
Marica
5f8d8c096c Merge pull request #31440 from frappe/mergify/bp/version-13-hotfix/pr-31421
fix: add UOM validation for planned-qty (backport #31421)
2022-06-24 13:02:44 +05:30
s-aga-r
559bde3135 fix: add UOM validation for planned-qty
(cherry picked from commit 00807abe31)
2022-06-24 07:10:55 +00:00
Deepesh Garg
d1bb94bb1d Merge pull request #31431 from frappe/mergify/bp/version-13-hotfix/pr-31395
fix: filter set in Bank Clearance Summary (backport #31395)
2022-06-23 14:26:45 +05:30
Rucha Mahabal
abfe926a45 fix(Salary Slip): Components not updated when amount evaluates to 0 due to payment days (#31425)
* fix(Salary Slip): Components not updated when amount evaluates to 0 due to payment days

* fix: remove 0 valued components that have been updated later
2022-06-23 11:01:00 +05:30
Nihantra C. Patel
4b53ec22c5 Update bank_clearance_summary.py
(cherry picked from commit 0097a2b60c)
2022-06-23 05:25:17 +00:00
Marica
87f77813ea Merge pull request #31422 from frappe/mergify/bp/version-13-hotfix/pr-31420
fix: translation for filter status on report (backport #31420)
2022-06-22 14:01:42 +05:30
Marica
b4e64d0ebb Merge pull request #31423 from marination/bom-update-log-cleanup-perf-hotfix
chore: Clear Progress section for completed logs & `on_submit` UX
2022-06-22 14:01:19 +05:30
marination
fcec318588 chore: Clear Progress section for completed logs & on_submit UX
- Delete `BOM Update Batch` table on 'Completed' log, to save space
- Hide Progress section on 'Completed' log
- Enqueue `on_submit` for 'Update Cost' job, getting leaf boms could take time for huge DBs. Users have to wait for screen to unfreeze.
- Add error handling to `process_boms_cost_level_wise` (Called via cron job and on submit, both in background)
2022-06-22 12:27:08 +05:30
hrzzz
736f20656d fix: translation for filter status on report
(cherry picked from commit 8b1ff96e30)
2022-06-22 06:16:52 +00:00
mergify[bot]
8b78a122e7 fix: update ru translate (backport #31404) (#31417)
* fix: update ru translate (#31404)

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

fix logic

* 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

* Update ru.csv

* Update ru.csv

(cherry picked from commit ce1b4e40a1)

# Conflicts:
#	erpnext/translations/ru.csv

* chore: Fix merge conflicts

Co-authored-by: Vladislav <integration.into.society@gmail.com>
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2022-06-21 20:14:13 +05:30
Marica
1e34616c91 Merge pull request #31393 from frappe/mergify/bp/version-13-hotfix/pr-30837
fix: Respect system precision for user facing balance qty values (backport #30837)
2022-06-21 20:13:23 +05:30
Frappe PR Bot
0a25d28e34 chore(release): Bumped to Version 13.34.1
## [13.34.1](https://github.com/frappe/erpnext/compare/v13.34.0...v13.34.1) (2022-06-21)

### Bug Fixes

* Conversion rate validation for multi-currency invoices ([4186aaf](4186aafd07))
* **india:** e-invoice eligibility if company gstin is not configured ([#31247](https://github.com/frappe/erpnext/issues/31247)) ([7696ae4](7696ae4de1))
* internal transfer GLE validation ([8a4cd2a](8a4cd2aba1))
* Merge Conflicts ([0f3a02d](0f3a02db57))
* Pick Template BOM if variant BOM absent in WO popup from SO ([7c35887](7c35887d07))
* Quotation lost update ([3b9f943](3b9f943335))
* Spelling mistake in quotation depend on (backport [#31362](https://github.com/frappe/erpnext/issues/31362)) ([#31363](https://github.com/frappe/erpnext/issues/31363)) ([ebcdaf7](ebcdaf7c82))
* transaction date gets unset in material request ([#31387](https://github.com/frappe/erpnext/issues/31387)) ([4e2ed6f](4e2ed6f9d9)), closes [#31327](https://github.com/frappe/erpnext/issues/31327)
* UOM handling for transaction without item (backport [#31389](https://github.com/frappe/erpnext/issues/31389)) ([#31391](https://github.com/frappe/erpnext/issues/31391)) ([051e5cd](051e5cd741))

### Performance Improvements

* GLE reposting with progress and chunking (backport [#31343](https://github.com/frappe/erpnext/issues/31343)) ([#31373](https://github.com/frappe/erpnext/issues/31373)) ([f19ed0b](f19ed0b74c))
2022-06-21 11:14:34 +00:00
s-aga-r
def622c13e fix: set default_bom for item
(cherry picked from commit dc2830da4d)

# Conflicts:
#	erpnext/manufacturing/doctype/bom/test_bom.py
2022-06-21 11:13:31 +00:00
Ankush Menat
d6d2215d8e Merge pull request #31414 from frappe/version-13-hotfix
chore: weekly version-13 release
2022-06-21 16:39:04 +05:30
Deepesh Garg
e43e442448 Merge pull request #31413 from frappe/mergify/bp/version-13-hotfix/pr-31264
fix: internal transfer GLE validation (backport #31264)
2022-06-21 14:56:56 +05:30
Ankush Menat
8a4cd2aba1 fix: internal transfer GLE validation
(cherry picked from commit 65b21ee7d6)
2022-06-21 09:02:02 +00:00
Marica
3f4d00d288 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-30837 2022-06-21 11:02:46 +05:30
Deepesh Garg
8b3fc15ca8 Merge pull request #31397 from frappe/mergify/bp/version-13-hotfix/pr-31396
fix: Quotation lost update (backport #31396)
2022-06-21 10:28:45 +05:30
Deepesh Garg
760ba0877c Merge pull request #31411 from frappe/mergify/bp/version-13-hotfix/pr-31350
fix: Conversion rate validation for multi-currency invoices (backport #31350)
2022-06-21 10:28:15 +05:30
Deepesh Garg
da70b69d68 test: Add test case
(cherry picked from commit 8f37393044)
2022-06-21 03:40:13 +00:00
Deepesh Garg
4186aafd07 fix: Conversion rate validation for multi-currency invoices
(cherry picked from commit d05d15346a)
2022-06-21 03:40:13 +00:00
marination
70a7dfe2f0 chore: Missing import flt 2022-06-20 17:18:01 +05:30
Marica
e982abbe4d chore: Change autoname to v13 compatible value (#31407)
- 'autoincrement' is present only in v14
- 'hash' is automatically still used in tables but change value for safety
2022-06-20 17:09:52 +05:30
marination
5bcfbbe124 chore: fix merge conflict 2022-06-20 16:59:18 +05:30
Deepesh Garg
8e2e61ef84 chore: resolve conflicts 2022-06-18 20:23:58 +05:30
Deepesh Garg
b3169edfc8 Merge pull request #31275 from frappe/mergify/bp/version-13-hotfix/pr-31247
fix(india): e-invoice eligibility if company gstin is not configured (backport #31247)
2022-06-17 21:41:44 +05:30
Deepesh Garg
8d63abb442 chore: fix condition
(cherry picked from commit e457288dba)

# Conflicts:
#	erpnext/selling/doctype/quotation/quotation.py
2022-06-17 15:39:53 +00:00
Deepesh Garg
3b9f943335 fix: Quotation lost update
(cherry picked from commit 02f9441e1a)

# Conflicts:
#	erpnext/selling/doctype/quotation/quotation.py
2022-06-17 15:39:52 +00:00
Marica
642b9c5466 fix: Respect system precision for user facing balance qty values (#30837)
* fix: Respect system precision for user facing balance qty values

- `get_precision` -> `set_precision`
- Use system wide currency precision for `stock_value`
- Round of qty defiiciency as per user defined precision (system flt precision), so that it is WYSIWYG for users

* fix: Consider system precision when validating future negative qty

* test: Immediate Negative Qty precision test

- Test for Immediate Negative Qty precision
- Stock Entry Negative Qty message: Format available qty in system precision
- Pass `stock_uom` as confugrable option in `make_item`

* test: Future Negative Qty validation with precision

* fix: Use `get_field_precision` for currency precision as it used to

- `get_field_precision` defaults to number format for precision (maintain old behaviour)
- Don't pass `currency` to `get_field_precision` as its not used anymore

(cherry picked from commit d6078aa911)

# Conflicts:
#	erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
2022-06-17 09:46:09 +00:00
mergify[bot]
051e5cd741 fix: UOM handling for transaction without item (backport #31389) (#31391)
fix: UOM handling for transaction without item (#31389)

If invoice is made without item code then UOM, Stock UOM and
conversion_factor all need to be manually added, this is confusing and
leads missing them out leads to errors.

Simplest solution:

- if either UOM exists then set both to same uom conversion factor to
- also set conversion factor based on UOM conversions

(cherry picked from commit 10583eb3ce)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-06-17 15:11:28 +05:30
mergify[bot]
4e2ed6f9d9 fix: transaction date gets unset in material request (#31387)
* fix: transaction date gets unset in material request (#31327)

* fix: set date correctly in material request

* fix: use only `transaction_date` in `get_item_details`

* fix: resolve merge conflict

Co-authored-by: Sagar Vora <sagar@resilient.tech>
2022-06-17 12:04:10 +05:30
Marica
2d6e518f87 Merge pull request #31380 from frappe/mergify/bp/version-13-hotfix/pr-31372
chore: Fix a potential variable misuse bug (backport #31372)
2022-06-16 12:47:58 +05:30
Marica
0f3a02db57 fix: Merge Conflicts 2022-06-16 12:23:13 +05:30
Jingxuan He
2cd03196a4 chore: Fix a potential variable misuse bug (#31372)
* Fix a potential variable misuse bug

* chore: Separate check (separate line) for empty table in Pricing Rule

* chore: Code readability & check for field in row (now row itself)

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

# Conflicts:
#	erpnext/accounts/doctype/pricing_rule/pricing_rule.py
2022-06-16 06:47:49 +00:00
Ankush Menat
f19ed0b74c perf: GLE reposting with progress and chunking (backport #31343) (#31373)
* fix: dont use cached doc for GLE reposts

ported from https://github.com/frappe/erpnext/pull/31240

* perf: GLE reposting with progress and chunking

If stock voucher count goes >1000 then fetching all gles and reposting
them all at once requires much more memory and can cause crash.

- This PR ensures that GLE reposting is done in chunks of 100 vouchers.
- This PR also starts keeping track of how many such chunks were
  processed so in future progress is resumed in event of timeout.

* test: add "actual" test for chunked GLE reposting
2022-06-15 20:54:20 +05:30
Marica
001130c0da Merge pull request #31368 from frappe/mergify/bp/version-13-hotfix/pr-31353
fix: Pick Template BOM if variant BOM absent in WO popup from SO (backport #31353)
2022-06-15 15:42:58 +05:30
marination
7c35887d07 fix: Pick Template BOM if variant BOM absent in WO popup from SO
- Use `get_default_bom` in sales_order.py (reduce duplicate utility functions)
- Remove redundant if else in `get_work_order_items`
- `get_default_bom`: If no BOM and template exists try to fetch template BOM
- test: `get_work_order_items` via SO and if right BOM is picked

(cherry picked from commit 9f2d325e67)
2022-06-15 09:07:54 +00:00
mergify[bot]
ebcdaf7c82 fix: Spelling mistake in quotation depend on (backport #31362) (#31363)
* fix: Spelling mistake in quotation depend on (#31362)

Update quotation.json

(cherry picked from commit 37e9622426)

* chore: bump modified

Co-authored-by: Nihantra C. Patel <99652762+nihantra@users.noreply.github.com>
Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-06-15 12:05:46 +05:30
Frappe PR Bot
943d83b739 chore(release): Bumped to Version 13.34.0
# [13.34.0](https://github.com/frappe/erpnext/compare/v13.33.0...v13.34.0) (2022-06-14)

### Bug Fixes

* (auto-merge) Use `frappe.log_error` instead of `doc.log_error` ([bced6a0](bced6a07b4))
* `test_work_order_with_non_stock_item` ([b827c3b](b827c3b3c9))
* Add cost center in loan document ([c19dfbe](c19dfbe98a))
* **asset:** failing test case ([#31277](https://github.com/frappe/erpnext/issues/31277)) ([8a4c9d1](8a4c9d1238))
* Auto Insert Item Price If Missing when discount & blank UOM (backport [#31168](https://github.com/frappe/erpnext/issues/31168)) ([#31267](https://github.com/frappe/erpnext/issues/31267)) ([e69bff0](e69bff0caa))
* Call `calculate_cost` for Draft BOM and typo in argument ([d035aa2](d035aa2afb))
* Company address filter in quotation ([d375939](d375939475))
* DB update child items, remove redundancy, fix perf ([cbc52a2](cbc52a2e45))
* Depreciate Asset before generating GL Entries on sale ([#30759](https://github.com/frappe/erpnext/issues/30759)) ([dfbfe40](dfbfe403e9))
* Get fresh RM rate in `calculate_rm_cost` ([9a513fd](9a513fda74))
* **India:** Incorrect taxable in GSTR-3B report ([e5d2c59](e5d2c59929))
* **India:** Sales taxes and charges template fetching in quotation ([a1ba847](a1ba8475d0))
* locale Currency and Float setting in update_employee ([391ed9c](391ed9c567))
* misaligned columns in print format of AR/AP report ([81e32e2](81e32e2855))
* Partially Ordered status for quotation ([37ba550](37ba550398))
* purchase invoice standalone return GLEs (backport [#31209](https://github.com/frappe/erpnext/issues/31209)) ([#31263](https://github.com/frappe/erpnext/issues/31263)) ([6d99b5a](6d99b5a95a))
* Reset represents company on disabling internal customer and supplier (backport [#31302](https://github.com/frappe/erpnext/issues/31302)) ([#31306](https://github.com/frappe/erpnext/issues/31306)) ([fb9b302](fb9b302ecf))
* Safe cast `row.rate` (in case of faulty exploded items, edge case but oh well) ([6d65e2b](6d65e2bab4))
* Supplied Qty not updated on Stock Entry cancel ([79b2062](79b20622c9))
* Trial Balance failing to ignore Finance Book ([00371f4](00371f4a22))
* typo in sql condition ([894f945](894f945be7))
* update fr translation (backport [#31232](https://github.com/frappe/erpnext/issues/31232)) ([#31334](https://github.com/frappe/erpnext/issues/31334)) ([5d0f271](5d0f271451))
* update Period Closing Voucher per Company ([5ebbe81](5ebbe81543))
* update ru translate (backport [#31200](https://github.com/frappe/erpnext/issues/31200)) ([#31304](https://github.com/frappe/erpnext/issues/31304)) ([77e4755](77e4755c1f))
* Use `frappe.as_unicode` to decode output of redis module list (backport [#31282](https://github.com/frappe/erpnext/issues/31282)) ([#31283](https://github.com/frappe/erpnext/issues/31283)) ([633a452](633a4521e4))
* **ux:** hide new version btn on unsaved BOM (backport [#31297](https://github.com/frappe/erpnext/issues/31297)) ([#31298](https://github.com/frappe/erpnext/issues/31298)) ([9f6b32a](9f6b32af12))
* **UX:** use doc.status for Job Card status ([#31320](https://github.com/frappe/erpnext/issues/31320)) ([78473b8](78473b8d99))

### Features

* Add german translations ([a3f2cf3](a3f2cf3917))
* Level-wise BOM cost updation ([74d7d81](74d7d81d6e))
* Only update exploded items rate and amount ([a26da58](a26da58718))
* Track progress in Log Batch/Job wise ([3b2a8bf](3b2a8bf837))

### Performance Improvements

* `get_boms_in_bottom_up_order` ([87c2b3b](87c2b3be0b))
* `get_next_higher_level_boms` ([767a775](767a775709))
* Use cached doc instead of `get_doc` ([faa69c9](faa69c942b))
2022-06-14 07:45:40 +00:00
Deepesh Garg
662a63b3f5 Merge pull request #31349 from frappe/version-13-hotfix
chore: weekly version-13 release
2022-06-14 13:13:45 +05:30
Deepesh Garg
55e0c03ef0 Merge pull request #31347 from frappe/mergify/bp/version-13-hotfix/pr-31322
fix: Company address filter in quotation (backport #31322)
2022-06-14 12:44:59 +05:30
Deepesh Garg
da1a948a28 chore: resolve conflicts 2022-06-14 11:36:43 +05:30
Deepesh Garg
823cf88c3c chore: linting issues
(cherry picked from commit fb3da124e5)
2022-06-14 05:47:58 +00:00
Deepesh Garg
37ba550398 fix: Partially Ordered status for quotation
(cherry picked from commit 118c786e63)

# Conflicts:
#	erpnext/selling/doctype/quotation/quotation.json
2022-06-14 05:47:57 +00:00
Deepesh Garg
a1ba8475d0 fix(India): Sales taxes and charges template fetching in quotation
(cherry picked from commit 243625898e)
2022-06-14 05:47:55 +00:00
Deepesh Garg
d375939475 fix: Company address filter in quotation
(cherry picked from commit 2fc04f661a)
2022-06-14 05:47:54 +00:00
Marica
c5bda9cb09 Merge pull request #31344 from frappe/mergify/bp/version-13-hotfix/pr-31341
fix: Supplied Qty not updated on Stock Entry cancel (backport #31341)
2022-06-13 23:24:49 +05:30
Marica
6064ca6fed test: Pass "yes" instead of 1 for is_subcontracted in create_purchase_order 2022-06-13 21:06:04 +05:30
marination
86a0ba5c9f test: PO Supplied Qty reset on cancel/submit
(cherry picked from commit b8f468cb4f)
2022-06-13 15:13:54 +00:00
marination
79b20622c9 fix: Supplied Qty not updated on Stock Entry cancel
- Loop over PO supplied items and update them as data from SE will exclude a row if supplied qty becomes 0 on cancel
- Use DB API insteaf of raw SQL

(cherry picked from commit fa1d9d548e)
2022-06-13 15:13:53 +00:00
Saqib Ansari
fb1325d7a5 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31247 2022-06-13 17:08:23 +05:30
mergify[bot]
5d0f271451 fix: update fr translation (backport #31232) (#31334)
* fix: update fr translation (#31232)

* update fr translation

* fix:update fr translation

* fix:update fr translation

* fix:update fr translation

* fix:update fr translation

* fix:update fr translation

* fix:update fr translation

* fix:update fr translation

* Update fr.csv

update typo

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* update fr translation

* fix: Use elision instead of HTML code equivalent

* fix: Use elision instead of HTML code equivalent (pt 2)

* fix: Use elision/single quote instead of HTML code equivalent (pt 3)

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

* fix: Accidental '=' instead of comma

Co-authored-by: HENRY Florian <florian.henry@open-concept.pro>
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2022-06-13 12:18:46 +05:30
Marica
1e6e652988 Merge pull request #31314 from frappe/mergify/bp/version-13-hotfix/pr-31303
fix: locale Currency and Float setting in update_employee (backport #31303)
2022-06-13 10:49:29 +05:30
Marica
ff770bbf04 Merge pull request #31321 from frappe/mergify/bp/version-13-hotfix/pr-31320
fix(UX): use doc.status for Job Card status (backport #31320)
2022-06-13 10:30:05 +05:30
Ankush Menat
78473b8d99 fix(UX): use doc.status for Job Card status (#31320)
- Use doc.status directly for indicator - single source of truth
- Update status to cancelled when doc is cancelled

(cherry picked from commit 39ec0aca95)
2022-06-13 10:04:53 +05:30
Deepesh Garg
977d6969d5 Merge pull request #31318 from frappe/mergify/bp/version-13-hotfix/pr-31295
fix: update Period Closing Voucher per Company (backport #31295)
2022-06-11 23:44:22 +05:30
mergify[bot]
50a4c2e9dc refactor: remove add_fetch (backport #31315) (#31319)
* refactor: remove add_fetch (#31315)

- Sales Team already had fetch from set up
- Set up fetch from on sales partner in sales transaction

Reason for removal: the JS code applies arbitrarily to any field called "sales_person"

(cherry picked from commit 1646fbe478)

# Conflicts:
#	erpnext/selling/sales_common.js

* chore: conflicts

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-06-10 18:30:50 +05:30
hendrik
5ebbe81543 fix: update Period Closing Voucher per Company
Validate period closing voucher company-wise

(cherry picked from commit 74b274f555)
2022-06-10 09:23:36 +00:00
Deepesh Garg
395f86f42b Merge pull request #31310 from frappe/mergify/bp/version-13-hotfix/pr-31294
fix(India): Incorrect taxable in GSTR-3B report (backport #31294)
2022-06-10 14:27:20 +05:30
Deepesh Garg
9e0cb2a467 Merge pull request #31262 from frappe/mergify/bp/version-13-hotfix/pr-30155
fix: Add cost center in loan document (backport #30155)
2022-06-10 13:16:33 +05:30
Deepesh Garg
1aaca097b3 chore: Linting Issues 2022-06-10 12:51:16 +05:30
Deepesh Garg
309bdf257d Merge pull request #31139 from frappe/mergify/bp/version-13-hotfix/pr-31102
feat: Add german translations (backport #31102)
2022-06-10 12:42:10 +05:30
Deepesh Garg
4c9422fb1b chore: resolve conflicts 2022-06-10 12:39:12 +05:30
RJPvT
391ed9c567 fix: locale Currency and Float setting in update_employee
In fieldtypes locale settings (example NL) . and , changes whereby the field is inproperly filled

(cherry picked from commit 17887cde71)
2022-06-10 06:05:51 +00:00
Deepesh Garg
95f8784ea9 chore: Resolve conflicts 2022-06-10 10:59:46 +05:30
Deepesh Garg
176a6722e5 chore: cleanup
(cherry picked from commit 50aafdbe99)
2022-06-10 05:19:55 +00:00
Deepesh Garg
e5d2c59929 fix(India): Incorrect taxable in GSTR-3B report
(cherry picked from commit 20f568c159)
2022-06-10 05:19:54 +00:00
Deepesh Garg
384ce92b4b Merge pull request #31307 from frappe/mergify/bp/version-13-hotfix/pr-31216
fix: Trial Balance failing to ignore Finance Book (backport #31216)
2022-06-10 00:10:23 +05:30
Sun Howwrongbum
894f945be7 fix: typo in sql condition
(cherry picked from commit ee2949aa3f)
2022-06-09 15:05:15 +00:00
Deepesh Garg
44642dba39 chore: Linting Issues
(cherry picked from commit b9dbb36d0e)
2022-06-09 15:05:14 +00:00
Sun Howwrongbum
00371f4a22 fix: Trial Balance failing to ignore Finance Book
(cherry picked from commit 48bde2de2a)
2022-06-09 15:05:14 +00:00
mergify[bot]
fb9b302ecf fix: Reset represents company on disabling internal customer and supplier (backport #31302) (#31306)
fix: Reset represents company on disabling internal customer and supplier (#31302)

(cherry picked from commit c13e5ad741)

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2022-06-09 19:20:53 +05:30
mergify[bot]
77e4755c1f fix: update ru translate (backport #31200) (#31304)
* fix: update ru translate (#31200)

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

* Update ru.csv

fix logic

* Update ru.csv

* Update ru.csv

* Update ru.csv

(cherry picked from commit 2675751d6c)

# Conflicts:
#	erpnext/translations/ru.csv

* fix: Merge conflicts

Co-authored-by: Vladislav <integration.into.society@gmail.com>
Co-authored-by: Marica <maricadsouza221197@gmail.com>
2022-06-09 19:16:48 +05:30
Marica
bb431bb21f Merge pull request #31299 from frappe/mergify/bp/version-13-hotfix/pr-31296
fix: misaligned columns in print format of AR/AP report (backport #31296)
2022-06-09 18:31:20 +05:30
Marica
48de5ab62a Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31296 2022-06-09 18:09:09 +05:30
Marica
3816ae21bb Merge pull request #31078 from marination/v13-perf-bom-update-tool
perf: BOM Update Tool (backport)
2022-06-09 17:51:28 +05:30
meaziz
9347cbbc9f chore: Asset Arabic translation Fix (#31221)
Update ar.csv

Fix Translation arabic translation that caused an error when submitting an asset if user language was arabic
2022-06-09 17:43:31 +05:30
ruthra kumar
81e32e2855 fix: misaligned columns in print format of AR/AP report
(cherry picked from commit bbaa14af16)
2022-06-09 12:06:11 +00:00
mergify[bot]
9f6b32af12 fix(ux): hide new version btn on unsaved BOM (backport #31297) (#31298)
fix(ux): hide new version btn on unsaved BOM (#31297)

(cherry picked from commit d9a5213952)

Co-authored-by: Ankush Menat <me@ankush.dev>
2022-06-09 17:22:50 +05:30
marination
5a08850aa1 chore: Less hacky tests, versioning (replace bom) and clearing log data (update cost)
- Remove `auto_commit_on_many_writes` in `update_cost_in_level()` as commits happen every N BOMs
- Auto commit every 50 BOMs
- test: Remove hacky `frappe.flags.in_test` returns
- test: Enqueue `now` if in tests (for update cost and replace bom)
- Replace BOM: Copy bom object to `_doc_before_save` so that version.py finds a difference between the two
- Replace BOM: Add reference to version
- Update Cost: Unset `processed_boms` if Log is completed (useless after completion)
- test: `update_cost_in_all_boms_in_test` works close to actual prod implementation (only call Cron job manually)
- Test: use `enqueue_replace_bom`  so that test works closest to production behaviour

Co-authored-by: Ankush Menat <ankushmenat@gmail.com>
2022-06-09 16:56:19 +05:30
Marica
7a868a46d5 Merge branch 'version-13-hotfix' into v13-perf-bom-update-tool 2022-06-09 13:33:52 +05:30
mergify[bot]
633a4521e4 fix: Use frappe.as_unicode to decode output of redis module list (backport #31282) (#31283)
fix: Use `frappe.as_unicode` to decode output of redis module list (#31282)

- As of redis 7, a list is added to the result of fetching the module list
- This list cannot be "decoded",so use `frappe.as_unicode` that handles bytes as well as other types

(cherry picked from commit 2832731601)

Co-authored-by: Marica <maricadsouza221197@gmail.com>
2022-06-08 18:12:02 +05:30
Saqib Ansari
e1fe901a91 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31247 2022-06-08 14:21:31 +05:30
Saqib Ansari
8a4c9d1238 fix(asset): failing test case (#31277) 2022-06-08 14:12:59 +05:30
Marica
8656020c00 Merge branch 'version-13-hotfix' into v13-perf-bom-update-tool 2022-06-08 14:09:51 +05:30
marination
289d65a4ed chore: get_valuation_rate sider fixes
- Use qb instead of db.sql
- Don't use `args` as argument for function
- Cleaner variable names
2022-06-08 14:08:45 +05:30
marination
b529a610fb test: Fix test_update_bom_cost_in_all_boms
- Use base_rate for assertions as rate is subject to change due to conversion factor (USD)
2022-06-08 14:08:12 +05:30
marination
c7accb9c33 test: Util to update cost in all BOMs
- Utility to update cost in all BOMs without cron jobs or background jobs (run immediately)
- Re-use util wherever all bom costs are to be updated
- Skip explicit commits if in test
- Specify company in test records (dirty data sometimes, company wh mismatch)
- Skip background jobs queueing if in test
2022-06-08 14:07:34 +05:30
Saqib Ansari
7696ae4de1 fix(india): e-invoice eligibility if company gstin is not configured (#31247)
(cherry picked from commit fb4f8d870b)
2022-06-08 04:06:58 +00:00
Ganga Manoj
dfbfe403e9 fix: Depreciate Asset before generating GL Entries on sale (#30759) 2022-06-08 09:35:43 +05:30
mergify[bot]
e69bff0caa fix: Auto Insert Item Price If Missing when discount & blank UOM (backport #31168) (#31267)
fix: Auto Insert Item Price If Missing when discount & blank UOM (#31168)

* fix: Auto Insert Item Price If Missing when discount and blank UOM

fixes wrong item price insert when discount is used and adds uom=stock_uom instead of blank as price is converted to stock uom

* unit tests added for item with discount

I have added test  for auto_insert_price where discount is used.

* unit test issue fixed

fixed make_sales_order as some of the test that depended on it were failing due to passing of incorrect parameters.

Co-authored-by: Ankush Menat <me@ankush.dev>
(cherry picked from commit b3ccc4bfb9)

Co-authored-by: maharshivpatel <39730881+maharshivpatel@users.noreply.github.com>
2022-06-07 16:04:00 +05:30
Ankush Menat
0e53edfd49 test: sales register report with conditions (#31266) 2022-06-07 16:03:29 +05:30
mergify[bot]
6d99b5a95a fix: purchase invoice standalone return GLEs (backport #31209) (#31263)
* test: create stock test mixin for assertion/utils

(cherry picked from commit 293eb8d722)

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

* fix: purchase invoice return GLe

voucher_wise_stock_value contains tuples and the condition was looking
for string, so it's never triggered.

Caused by https://github.com/frappe/erpnext/pull/24200

(cherry picked from commit 7726271e2a)

* chore: conflicts

Co-authored-by: Ankush Menat <me@ankush.dev>
Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-06-07 15:43:37 +05:30
Frappe PR Bot
3b1f9fe03b chore(release): Bumped to Version 13.33.0
# [13.33.0](https://github.com/frappe/erpnext/compare/v13.32.0...v13.33.0) (2022-06-07)

### Bug Fixes

* Close unsecured terms loans ([d6d1d79](d6d1d79ba0))
* Consider only Approved leave applications in LWP, Employee Benefit calculations ([7f84c86](7f84c86d43))
* display currencies in validation message. ([ef22337](ef22337a9b))
* incorrect billed_qty in sales order analysis report when multiple delivery notes for item (backport [#31194](https://github.com/frappe/erpnext/issues/31194)) ([#31250](https://github.com/frappe/erpnext/issues/31250)) ([3b1f6da](3b1f6da741))
* incorrect LWP calculation for half days in employee benefit application ([10f0c93](10f0c935fe))
* **India:** GSTIN filter in GSTR-1 report ([abe9fe7](abe9fe70ce))
* **India:** Supplies from composite dealer not showing up ([15712c7](15712c742b))
* **job card:** only hold during draft state (backport [#31243](https://github.com/frappe/erpnext/issues/31243)) ([#31249](https://github.com/frappe/erpnext/issues/31249)) ([a7fc278](a7fc278b60))
* **Leave Application:** 'Cancelled' status shown as 'Open' in list view ([8b48d45](8b48d45286))
* leave balance for earned leaves in backdated Leave Application dashboard (backport [#31253](https://github.com/frappe/erpnext/issues/31253)) ([#31256](https://github.com/frappe/erpnext/issues/31256)) ([a22d92f](a22d92f946))
* only fetch membership expiry if not already set in `member.js` ([#31259](https://github.com/frappe/erpnext/issues/31259)) ([d433784](d433784171))
* Parent dimension filters in orders ([b3cbcd8](b3cbcd871b))
* Pluralize year text instead of optional bracket (backport [#31210](https://github.com/frappe/erpnext/issues/31210)) ([#31212](https://github.com/frappe/erpnext/issues/31212)) ([b31709c](b31709c793))
* re-validate warehouse after 'update items' (backport [#31203](https://github.com/frappe/erpnext/issues/31203)) ([#31206](https://github.com/frappe/erpnext/issues/31206)) ([3a718c7](3a718c7d5f))
* remove leave policy assignment creation patch (backport [#31097](https://github.com/frappe/erpnext/issues/31097)) ([#31204](https://github.com/frappe/erpnext/issues/31204)) ([4aeb448](4aeb448fea))
* Remove redundant query ([3c4cf59](3c4cf5929f))
* Reverse provisional entries on Purchase Invoice cancel ([1fba432](1fba432786))
* **Sales Register:** incorrect query with dimensions (backport [#31242](https://github.com/frappe/erpnext/issues/31242)) ([#31251](https://github.com/frappe/erpnext/issues/31251)) ([0badfc8](0badfc8748))
* Simply cancel reverse entries ([100b8d9](100b8d9b96))

### Features

* **india:** Improve E-way Bill Cancellation. ([#31088](https://github.com/frappe/erpnext/issues/31088)) ([622d25e](622d25e126))
2022-06-07 09:59:51 +00:00
Deepesh Garg
5f7f8361d9 Merge pull request #31265 from frappe/version-13-hotfix
chore: weekly version-13 release
2022-06-07 15:28:17 +05:30
Deepesh Garg
f087246e38 Merge pull request #31261 from frappe/mergify/bp/version-13-hotfix/pr-31258
fix: Close unsecured terms loans (backport #31258)
2022-06-07 14:52:47 +05:30
Deepesh Garg
c19dfbe98a fix: Add cost center in loan document
(cherry picked from commit 5d66cc4c4a)

# Conflicts:
#	erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py
#	erpnext/patches.txt
2022-06-07 08:40:48 +00:00
Deepesh Garg
ef8483d2ea chore: Resolve conflicts 2022-06-07 14:10:44 +05:30
Deepesh Garg
8cb85fa90b Merge pull request #31260 from frappe/mergify/bp/version-13-hotfix/pr-31233
fix: Reverse provisional entries on Purchase Invoice cancel (backport #31233)
2022-06-07 14:03:44 +05:30
Rucha Mahabal
d433784171 fix: only fetch membership expiry if not already set in member.js (#31259) 2022-06-07 14:01:12 +05:30
Deepesh Garg
d6d1d79ba0 fix: Close unsecured terms loans
(cherry picked from commit 815141bf57)

# Conflicts:
#	erpnext/loan_management/doctype/loan/loan.py
2022-06-07 08:24:23 +00:00
Deepesh Garg
42a0b82c71 test: Add test coverage for cancellation
(cherry picked from commit dc8e80ea81)
2022-06-07 08:01:46 +00:00
Deepesh Garg
100b8d9b96 fix: Simply cancel reverse entries
(cherry picked from commit 86a24f3d22)
2022-06-07 08:01:45 +00:00
Deepesh Garg
1fba432786 fix: Reverse provisional entries on Purchase Invoice cancel
(cherry picked from commit 61fa4eb6c9)
2022-06-07 08:01:45 +00:00
Rucha Mahabal
149c6031a1 chore: add missing import 2022-06-07 11:08:29 +05:30
Rucha Mahabal
ad1b419368 test: Employee Benefit Application
- make `get_no_of_days` a function for reusability
2022-06-07 11:08:29 +05:30
Rucha Mahabal
10f0c935fe fix: incorrect LWP calculation for half days in employee benefit application 2022-06-07 11:08:29 +05:30
Rucha Mahabal
8b48d45286 fix(Leave Application): 'Cancelled' status shown as 'Open' in list view 2022-06-07 11:08:29 +05:30
Rucha Mahabal
144d71c6af refactor: rewrite lwp queries using query builder 2022-06-07 11:08:29 +05:30
Rucha Mahabal
7f84c86d43 fix: Consider only Approved leave applications in LWP, Employee Benefit calculations
- do not allow submitting leave applications with 'Cancelled' status
2022-06-07 11:08:29 +05:30
mergify[bot]
a22d92f946 fix: leave balance for earned leaves in backdated Leave Application dashboard (backport #31253) (#31256)
fix: leave balance for earned leaves in backdated Leave Application dashboard

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-06-07 10:22:27 +05:30
Deepesh Garg
86856491b7 Merge pull request #31235 from frappe/mergify/bp/version-13-hotfix/pr-31219
fix: Parent dimension filters in orders (backport #31219)
2022-06-06 20:53:33 +05:30
Deepesh Garg
60d378aed2 chore: Resolve conflicts 2022-06-06 20:36:40 +05:30
mergify[bot]
3b1f6da741 fix: incorrect billed_qty in sales order analysis report when multiple delivery notes for item (backport #31194) (#31250)
* fix: incorrect billed_qty when item has multiple Delivery note

sales order analysis report returns incorrect billed_qty value for
an SO item has multiple delivery notes

(cherry picked from commit 0331e37982)

* test: multiple delivery notes and billed quantity

(cherry picked from commit 4f1bfbb93d)

Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2022-06-06 19:55:18 +05:30
mergify[bot]
a7fc278b60 fix(job card): only hold during draft state (backport #31243) (#31249)
* fix(job card): only hold during draft state (#31243)

(cherry picked from commit ee5bc58e9b)

# Conflicts:
#	erpnext/patches.txt

* chore: conflicts

Co-authored-by: Ankush Menat <me@ankush.dev>
2022-06-06 19:54:56 +05:30
mergify[bot]
0badfc8748 fix(Sales Register): incorrect query with dimensions (backport #31242) (#31251)
fix(Sales Register): incorrect query with dimensions

If accounting dimension is also part of the default filters then same
query is repeated with incorrect syntax.

e.g. `item_group = (child1, child2)` instead of `in` query.

fix: don't add default filter if they are part of dimensions to be
added.

(cherry picked from commit c3219ebad1)

Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-06-06 19:54:41 +05:30
marination
d3b06a682c chore: get_valuation_rate in bom.py must always return float & goto Item master if no bins 2022-06-06 18:15:07 +05:30
marination
7bd5558c31 chore: Miscellanous fixes/enhancements
- `get_valuation_rate`: if no bins are found return 0, SLEs do not exist either
- `get_valuation_rate`: Compute average valuation rate via query
- `get_rm_rate_map`: set order_by as None to avoid creating sort index (modified) each time query runs (seen in process list)
- BOM Update Batch: add status field and hide `boms_updated` so that  users can see progress without loading all updated boms (too much data)
- BOM Update Batch: set batch row status to completed after job runs
- BOM Update Log: remove `parent_boms` field (just pass parent boms to processing function) & remove Paused state (not used)
- Move job to long queue to avoid choking default queue
- `update_cost_in_boms`: use `get_doc` as each BOM is accessed only once. Use `for_update` to lock BOM row
- Commit after every 100 BOMs
2022-06-06 17:04:06 +05:30
Deepesh Garg
b58c7750c6 Merge pull request #31237 from frappe/mergify/bp/version-13-hotfix/pr-31223
fix: display currencies in validation message. (backport #31223)
2022-06-06 14:48:21 +05:30
Deepesh Garg
aaaacf9a8f Merge pull request #31241 from frappe/mergify/bp/version-13-hotfix/pr-31230
fix: Remove redundant query (backport #31230)
2022-06-06 14:48:00 +05:30
Deepesh Garg
3c4cf5929f fix: Remove redundant query
(cherry picked from commit a200e7e1fb)
2022-06-06 06:54:41 +00:00
Deepesh Garg
ed89963d65 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31223 2022-06-06 12:24:34 +05:30
Deepesh Garg
281fe34457 Merge pull request #31236 from frappe/mergify/bp/version-13-hotfix/pr-31231
fix(India): Supplies from composite dealer not showing up (backport #31231)
2022-06-06 12:24:20 +05:30
Deepesh Garg
d07014340f Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31223 2022-06-06 11:50:30 +05:30
Deepesh Garg
fa0984f192 Merge pull request #31234 from frappe/mergify/bp/version-13-hotfix/pr-31218
fix(India): GSTIN filter in GSTR-1 report (backport #31218)
2022-06-06 11:45:41 +05:30
Deepesh Garg
3ea2c95768 Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31223 2022-06-06 10:42:48 +05:30
Deepesh Garg
23d94c135f Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31231 2022-06-06 10:42:41 +05:30
Deepesh Garg
66bc9a974e Merge branch 'version-13-hotfix' into mergify/bp/version-13-hotfix/pr-31218 2022-06-06 10:42:28 +05:30
Deepesh Garg
91716e9c26 Merge pull request #31238 from frappe/mergify/bp/version-13-hotfix/pr-31217
ci: stale apt cache (backport #31217)
2022-06-06 10:06:41 +05:30
Ankush Menat
2db12d7bfa ci: stale apt cache (#31217)
(cherry picked from commit c7efa3b44d)
2022-06-06 04:03:14 +00:00
Devin Slauenwhite
f4a4dacb22 chore: linter
(cherry picked from commit b061ea4cd2)
2022-06-06 03:37:41 +00:00
Devin Slauenwhite
ef22337a9b fix: display currencies in validation message.
(cherry picked from commit 3a1c923e76)
2022-06-06 03:37:40 +00:00
Deepesh Garg
15712c742b fix(India): Supplies from composite dealer not showing up
(cherry picked from commit db07831db7)
2022-06-06 03:30:59 +00:00
Deepesh Garg
b3cbcd871b fix: Parent dimension filters in orders
(cherry picked from commit 3f376cc3a5)

# Conflicts:
#	erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.js
2022-06-06 03:29:07 +00:00
Deepesh Garg
abe9fe70ce fix(India): GSTIN filter in GSTR-1 report
(cherry picked from commit f0ac394d6e)
2022-06-06 03:28:38 +00:00
marination
3b2a8bf837 feat: Track progress in Log Batch/Job wise
- This was done due to stale reads while the background jobs tried updating status of the log
- Added a table where all bom jobs within log will be tracked with what level they are processing
- Cron job will check if table jobs are all processed every 5 mins
- If yes, it will prepare parents and call `process_boms_cost_level_wise` to start next level
- If pending jobs, do nothing
- Current BOM Level is being tracked that helps adding rows to the table
- Individual bom cost jobs (that are queued) will process and update boms > will update BOM Update Batch table row with list of updated BOMs
2022-06-02 13:41:56 +05:30
mergify[bot]
b31709c793 fix: Pluralize year text instead of optional bracket (backport #31210) (#31212)
Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
Co-authored-by: Mohammad Hussain Nagaria <34810212+NagariaHussain@users.noreply.github.com>
2022-06-01 16:47:02 +05:30
mergify[bot]
3a718c7d5f fix: re-validate warehouse after 'update items' (backport #31203) (#31206)
fix: re-validate warehouse after 'update items' (#31203)

(cherry picked from commit c84e11ac82)

Co-authored-by: Ankush Menat <me@ankush.dev>
2022-06-01 15:24:55 +05:30
mergify[bot]
8b985d632f test: fix attendance tests for unmarked days (backport #31205) (#31208)
test: fix attendance tests for unmarked days (#31205)

* test: fix attendance tests for unmarked days

* chore: remove unused import

(cherry picked from commit 536f1dfc4b)

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-06-01 14:15:59 +05:30
mergify[bot]
4aeb448fea fix: remove leave policy assignment creation patch (backport #31097) (#31204)
* fix: remove leave policy assignment creation patch (#31097)

(cherry picked from commit d4b9cc0242)

# Conflicts:
#	erpnext/patches.txt

* chore: fix conflicts

Co-authored-by: Rucha Mahabal <ruchamahabal2@gmail.com>
2022-06-01 12:46:17 +05:30
Deepesh Garg
910ab405ae Merge pull request #31188 from frappe/mergify/bp/version-13-hotfix/pr-31088
feat(india): Check E-way Bill Status before Cancellation. (backport #31088)
2022-05-31 21:41:54 +05:30
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
marination
e6ad56cd68 chore: Limit Update Cost jobs & db_update only if changed values
- If `Update Cost` job is ongoing, then block creation of new ones since all BOMs are updated
- `db_update` in `calculate_rm_cost` only if changed values to reduce redundant row updates
- Misc: Use variable for batch size
2022-05-31 16:01:22 +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
maharshivpatel
622d25e126 feat(india): Improve E-way Bill Cancellation. (#31088)
(cherry picked from commit a8f98f3f96)
2022-05-31 06:45:23 +00:00
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
marination
bced6a07b4 fix: (auto-merge) Use frappe.log_error instead of doc.log_error
- The latter is only on develop
2022-05-27 22:06:12 +05:30
marination
6d65e2bab4 fix: Safe cast row.rate (in case of faulty exploded items, edge case but oh well) 2022-05-27 22:01:26 +05:30
marination
767a775709 perf: get_next_higher_level_boms
- Separate getting dependants and checking if they are valid (loop within loop led to redundant processing that slowed down function)
- Adding to above, the same dependant(parent) was repeatedly processed as many children shared it. Expensive.
- Use a parent-child map similar to child-parent map to check if all children are resolved
- `map.get()` reduced time: 10 mins -> 0.9s~1 second (as compared to `get_cached_doc` or query)
- Total time: 17 seconds to process 6599 leaf boms and 4.2L parent boms
- Previous Total time: >10 mins (I terminated it due to not wanting to waste time XD)
2022-05-27 21:16:07 +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
marination
12f0a9a183 chore: Change BOM Progress field types to Long Text 2022-05-27 17:05:29 +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
marination
a26da58718 feat: Only update exploded items rate and amount
- Generate RM-Rate map from Items table (will include subassembly items with rate)
- Function to reset exploded item rate from above map
- `db_update` exploded item rate only if rate is changed
- Via Update Cost, only update exploded items rate, do not regenerate table again
- Exploded Items are regenerated on Save and Replace BOM job
- `calculate_exploded_cost` is run only via non doc events (Update Cost button, Update BOMs Cost Job)
2022-05-27 15:10:50 +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
barredterra
a3f2cf3917 feat: Add german translations
(cherry picked from commit 2388d86623)

# Conflicts:
#	erpnext/translations/de.csv
2022-05-26 06:20:15 +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
marination
196a824c4f style: Update docstrings and fix/add type hints + Collapsible progress section in Log 2022-05-25 16:48:07 +05:30
marination
74d7d81d6e feat: Level-wise BOM cost updation
- Process BOMs level wise and Pause after level is complete
- Cron job will resume Paused jobs, which will again process the new level and pause at the end
- This will go on until all BOMs are updated
- Added Progress section with fields to track updated BOMs in Log
- Cleanup: Add BOM Updation utils file to contain helper functions/sub-functions
- Cleanup: BOM Update Log file will only contain functions that are in direct context of the Log

Co-authored-by: Gavin D'souza <gavin18d@gmail.com>
2022-05-25 16:47:54 +05:30
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
marination
b827c3b3c9 fix: test_work_order_with_non_stock_item
- Use the right price list and currency to avoid rate conversion (1000/62.9), since rates are reset correctly now
- Use RM rate based on Price List in BOM. Non stock item has no valuation
2022-05-20 12:34:15 +05:30
marination
9a513fda74 fix: Get fresh RM rate in calculate_rm_cost 2022-05-20 12:23:52 +05:30
marination
faa69c942b perf: Use cached doc instead of get_doc
- Doc is only used to iterate over items(which wont change) and change rate/amount of rows
- These changes are inserted in db via `db_update`, so no harm
- Tested locally: refetching cached doc after db update, reflects fresh data.
2022-05-20 12:23:42 +05:30
marination
d035aa2afb fix: Call calculate_cost for Draft BOM and typo in argument 2022-05-20 12:23:32 +05:30
marination
cbc52a2e45 fix: DB update child items, remove redundancy, fix perf
- Move `get_boms_in_bottom_up_order` in bom update tool’s file
- Remove repeated rm cost update from `update_cost`. `calculate_cost` handles RM cost update
- db_update children in `calculate_cost` optionally
- Don’t call `update_exploded_items` and regenerate exploded items in `update_cost`. They will stay the same (except cost)
2022-05-20 12:23:18 +05:30
marination
87c2b3be0b perf: get_boms_in_bottom_up_order
- Create child-parent map once and fetch value from child key to get parents
- Get parents recursively for a leaf node (get all ancestors)
- Approx. 44 secs for 4lakh 70k boms
2022-05-20 12:20:24 +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
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
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
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
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
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
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
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
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
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
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
1828 changed files with 270689 additions and 79373 deletions

View File

@@ -29,6 +29,9 @@ 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
E101, # ensured by use of black
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

@@ -66,6 +66,7 @@ ignore =
F841,
E713,
E712,
B023
max-line-length = 200

View File

@@ -2,9 +2,16 @@
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
sudo apt update && sudo apt install redis-server libcups2-dev
pip install frappe-bench

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

@@ -25,7 +25,7 @@ jobs:
fail-fast: false
matrix:
container: [1, 2, 3]
container: [1, 2]
name: Python Unit Tests
@@ -93,7 +93,7 @@ jobs:
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
- name: Run Tests
run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator
run: 'cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --total-builds 2 --build-number ${{ matrix.container }}'
env:
TYPE: server
CI_BUILD_ID: ${{ github.run_id }}

View File

@@ -1,117 +0,0 @@
name: UI
on:
pull_request:
paths-ignore:
- '**.md'
workflow_dispatch:
concurrency:
group: ui-v13-${{ github.event.number }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-18.04
timeout-minutes: 60
strategy:
fail-fast: false
name: UI Tests (Cypress)
services:
mysql:
image: mariadb:10.3
env:
MYSQL_ALLOW_EMPTY_PASSWORD: YES
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
steps:
- name: Clone
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: 3.7
- uses: actions/setup-node@v2
with:
node-version: 14
check-latest: true
- name: Add to Hosts
run: |
echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
- name: Cache pip
uses: actions/cache@v2
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
${{ runner.os }}-
- name: Cache node modules
uses: actions/cache@v2
env:
cache-name: cache-node-modules
with:
path: ~/.npm
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Cache cypress binary
uses: actions/cache@v2
with:
path: ~/.cache
key: ${{ runner.os }}-cypress-
restore-keys: |
${{ runner.os }}-cypress-
${{ runner.os }}-
- name: Install
run: bash ${GITHUB_WORKSPACE}/.github/helper/install.sh
env:
DB: mariadb
TYPE: ui
- name: Site Setup
run: cd ~/frappe-bench/ && bench --site test_site execute erpnext.setup.utils.before_tests
- name: cypress pre-requisites
run: cd ~/frappe-bench/apps/frappe && yarn add cypress-file-upload@^5 @testing-library/cypress@^8 --no-lockfile
- name: Build Assets
run: cd ~/frappe-bench/ && bench build
env:
CI: Yes
- name: UI Tests
run: cd ~/frappe-bench/ && bench --site test_site run-ui-tests erpnext --headless
env:
CYPRESS_RECORD_KEY: 60a8e3bf-08f5-45b1-9269-2b207d7d30cd
- name: Show bench console if tests failed
if: ${{ failure() }}
run: cat ~/frappe-bench/bench_run_logs.txt

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 -E "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

@@ -3,33 +3,31 @@
# These owners will be the default owners for everything in
# the repo. Unless a later match takes precedence,
erpnext/accounts/ @nextchamp-saqib @deepeshgarg007
erpnext/assets/ @nextchamp-saqib @deepeshgarg007
erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/assets/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/erpnext_integrations/ @nextchamp-saqib
erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007
erpnext/regional @nextchamp-saqib @deepeshgarg007
erpnext/selling @nextchamp-saqib @deepeshgarg007
erpnext/regional @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/selling @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/support/ @nextchamp-saqib @deepeshgarg007
pos* @nextchamp-saqib
erpnext/buying/ @marination @rohitwaghchaure @ankush
erpnext/e_commerce/ @marination
erpnext/maintenance/ @marination @rohitwaghchaure
erpnext/manufacturing/ @marination @rohitwaghchaure @ankush
erpnext/portal/ @marination
erpnext/quality_management/ @marination @rohitwaghchaure
erpnext/shopping_cart/ @marination
erpnext/stock/ @marination @rohitwaghchaure @ankush
erpnext/buying/ @rohitwaghchaure @s-aga-r
erpnext/maintenance/ @rohitwaghchaure @s-aga-r
erpnext/manufacturing/ @rohitwaghchaure @s-aga-r
erpnext/quality_management/ @rohitwaghchaure @s-aga-r
erpnext/stock/ @rohitwaghchaure @s-aga-r
erpnext/crm/ @ruchamahabal @pateljannat
erpnext/education/ @ruchamahabal @pateljannat
erpnext/healthcare/ @ruchamahabal @pateljannat @chillaranand
erpnext/hr/ @ruchamahabal @pateljannat
erpnext/healthcare/ @chillaranand
erpnext/hr/ @ruchamahabal
erpnext/non_profit/ @ruchamahabal
erpnext/payroll @ruchamahabal @pateljannat
erpnext/projects/ @ruchamahabal @pateljannat
erpnext/payroll @ruchamahabal
erpnext/projects/ @ruchamahabal
erpnext/controllers @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination
erpnext/patches/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure
erpnext/public/ @nextchamp-saqib @marination
.github/ @surajshetty3416 @ankush
requirements.txt @gavindsouza
.github/ @ankush
requirements.txt @gavindsouza @ankush

View File

@@ -65,6 +65,8 @@ GNU/General Public License (see [license.txt](license.txt))
The ERPNext code is licensed as GNU General Public License (v3) and the Documentation is licensed as Creative Commons (CC-BY-SA-3.0) and the copyright is owned by Frappe Technologies Pvt Ltd (Frappe) and Contributors.
By contributing to ERPNext, you agree that your contributions will be licensed under its GNU General Public License (v3).
---
## Contributing

View File

@@ -1,53 +1,60 @@
import inspect
import frappe
from erpnext.hooks import regional_overrides
__version__ = '13.23.0'
__version__ = "13.36.1"
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,17 @@ def allow_regional(fn):
return caller
@frappe.whitelist()
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,32 +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,31 +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:
@@ -158,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"
@@ -226,21 +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
@@ -248,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":
@@ -274,12 +358,21 @@ 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
@@ -289,92 +382,155 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
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:
@@ -384,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()
@@ -457,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

@@ -19,6 +19,7 @@
"book_asset_depreciation_entry_automatically",
"unlink_advance_payment_on_cancelation_of_order",
"enable_common_party_accounting",
"allow_multi_currency_invoices_against_single_party_account",
"post_change_gl_entries",
"enable_discount_accounting",
"tax_settings_section",
@@ -276,14 +277,21 @@
"fieldname": "enable_common_party_accounting",
"fieldtype": "Check",
"label": "Enable Common Party Accounting"
}
},
{
"default": "0",
"description": "Enabling this will allow creation of multi-currency invoices against single party account in company currency",
"fieldname": "allow_multi_currency_invoices_against_single_party_account",
"fieldtype": "Check",
"label": "Allow multi-currency invoices against single party account"
}
],
"icon": "icon-cog",
"idx": 1,
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2021-10-11 17:42:36.427699",
"modified": "2022-07-11 13:37:50.605141",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

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

@@ -22,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
@@ -76,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
@@ -152,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"
@@ -174,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:
@@ -208,84 +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))
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
@@ -302,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:
@@ -316,6 +391,7 @@ 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
@@ -328,109 +404,91 @@ def get_loan_vouchers(bank_account, transaction, document_types, 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")
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)
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,
)
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
.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')
)
query.where(loan_disbursement.disbursed_amount == filters.get("amount"))
else:
query.where(
loan_disbursement.disbursed_amount <= filters.get('amount')
)
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")
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)
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,
)
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.clearance_date.isnull()
).where(
loan_repayment.payment_account == bank_account
.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')
)
query.where(loan_repayment.amount_paid == filters.get("amount"))
else:
query.where(
loan_repayment.amount_paid <= filters.get('amount')
)
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
@@ -519,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"""
@@ -544,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,8 +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", "Loan Repayment",
"Loan Disbursement"]:
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":
@@ -57,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
@@ -101,48 +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)
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")
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)
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")
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")
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,13 +35,15 @@ 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
self.validate_and_set_fiscal_year()
self.pl_must_have_cost_center()
if not self.flags.from_repost:
if not self.flags.from_repost and self.voucher_type != "Period Closing Voucher":
self.check_mandatory()
self.validate_cost_center()
self.check_pl_account()
@@ -48,7 +52,7 @@ class GLEntry(Document):
def on_update(self):
adv_adj = self.flags.adv_adj
if not self.flags.from_repost:
if not self.flags.from_repost and self.voucher_type != "Period Closing Voucher":
self.validate_account_details(adv_adj)
self.validate_dimensions_for_pl_and_bs()
self.validate_allowed_dimensions()
@@ -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
# Didn't use db_set for optimization purpose
ref_doc.outstanding_amount = bal
frappe.db.set_value(against_voucher_type, against_voucher, 'outstanding_amount', bal)
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

@@ -149,22 +149,6 @@ frappe.ui.form.on("Journal Entry", {
}
});
}
else if(frm.doc.voucher_type=="Opening Entry") {
return frappe.call({
type:"GET",
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_opening_accounts",
args: {
"company": frm.doc.company
},
callback: function(r) {
frappe.model.clear_table(frm.doc, "accounts");
if(r.message) {
update_jv_details(frm.doc, r.message);
}
cur_frm.set_value("is_opening", "Yes");
}
});
}
}
},
@@ -189,8 +173,8 @@ frappe.ui.form.on("Journal Entry", {
var update_jv_details = function(doc, r) {
$.each(r, function(i, d) {
var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts");
row.account = d.account;
row.balance = d.balance;
frappe.model.set_value(row.doctype, row.name, "account", d.account)
frappe.model.set_value(row.doctype, row.name, "balance", d.balance)
});
refresh_field("accounts");
}

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",
@@ -136,7 +137,8 @@
"fieldname": "finance_book",
"fieldtype": "Link",
"label": "Finance Book",
"options": "Finance Book"
"options": "Finance Book",
"read_only": 1
},
{
"fieldname": "2_add_edit_gl_entries",
@@ -524,13 +526,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-06-23 22:01:32.348337",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",
@@ -578,6 +587,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

@@ -2,7 +2,7 @@
// For license information, please see license.txt
frappe.ui.form.on("Journal Entry Template", {
setup: function(frm) {
refresh: function(frm) {
frappe.model.set_default_values(frm.doc);
frm.set_query("account" ,"accounts", function(){

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

@@ -49,7 +49,15 @@ frappe.ui.form.on('Opening Invoice Creation Tool', {
doc: frm.doc,
btn: $(btn_primary),
method: "make_invoices",
freeze_message: __("Creating {0} Invoice", [frm.doc.invoice_type])
freeze: 1,
freeze_message: __("Creating {0} Invoice", [frm.doc.invoice_type]),
callback: function(r) {
if (r.message.length == 1) {
frappe.msgprint(__("{0} Invoice created successfully.", [frm.doc.invoice_type]));
} else if (r.message.length < 50) {
frappe.msgprint(__("{0} Invoices created successfully.", [frm.doc.invoice_type]));
}
}
});
});

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, # important: https://github.com/frappe/erpnext/pull/23559
"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

@@ -14,6 +14,7 @@ from erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_crea
test_dependencies = ["Customer", "Supplier", "Accounting Dimension"]
class TestOpeningInvoiceCreationTool(FrappeTestCase):
@classmethod
def setUpClass(self):
@@ -22,10 +23,24 @@ class TestOpeningInvoiceCreationTool(FrappeTestCase):
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()
@@ -68,15 +83,30 @@ class TestOpeningInvoiceCreationTool(FrappeTestCase):
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")
@@ -84,28 +114,37 @@ class TestOpeningInvoiceCreationTool(FrappeTestCase):
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"],
@@ -117,40 +156,44 @@ class TestOpeningInvoiceCreationTool(FrappeTestCase):
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")
@@ -163,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,32 +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"))
existing_party_link = frappe.get_all('Party Link', {
'primary_party': self.secondary_party
}, pluck="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"
)
if existing_party_link:
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")
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"
)
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

@@ -226,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();
},
@@ -1114,7 +1111,7 @@ frappe.ui.form.on('Payment Entry', {
$.each(tax_fields, function(i, fieldname) { tax[fieldname] = 0.0; });
frm.doc.paid_amount_after_tax = frm.doc.paid_amount;
frm.doc.paid_amount_after_tax = frm.doc.base_paid_amount;
});
},
@@ -1205,7 +1202,7 @@ frappe.ui.form.on('Payment Entry', {
}
cumulated_tax_fraction += tax.tax_fraction_for_current_item;
frm.doc.paid_amount_after_tax = flt(frm.doc.paid_amount/(1+cumulated_tax_fraction))
frm.doc.paid_amount_after_tax = flt(frm.doc.base_paid_amount/(1+cumulated_tax_fraction))
});
},
@@ -1237,6 +1234,7 @@ frappe.ui.form.on('Payment Entry', {
frm.doc.total_taxes_and_charges = 0.0;
frm.doc.base_total_taxes_and_charges = 0.0;
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
let actual_tax_dict = {};
// maintain actual tax rate based on idx
@@ -1257,8 +1255,8 @@ frappe.ui.form.on('Payment Entry', {
}
}
tax.tax_amount = current_tax_amount;
tax.base_tax_amount = tax.tax_amount * frm.doc.source_exchange_rate;
// tax accounts are only in company currency
tax.base_tax_amount = current_tax_amount;
current_tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0;
if(i==0) {
@@ -1267,9 +1265,29 @@ frappe.ui.form.on('Payment Entry', {
tax.total = flt(frm.doc["taxes"][i-1].total + current_tax_amount, precision("total", tax));
}
tax.base_total = tax.total * frm.doc.source_exchange_rate;
frm.doc.total_taxes_and_charges += current_tax_amount;
frm.doc.base_total_taxes_and_charges += current_tax_amount * frm.doc.source_exchange_rate;
// tac accounts are only in company currency
tax.base_total = tax.total
// calculate total taxes and base total taxes
if(frm.doc.payment_type == "Pay") {
// tax accounts only have company currency
if(tax.currency != frm.doc.paid_to_account_currency) {
//total_taxes_and_charges has the target currency. so using target conversion rate
frm.doc.total_taxes_and_charges += flt(current_tax_amount / frm.doc.target_exchange_rate);
} else {
frm.doc.total_taxes_and_charges += current_tax_amount;
}
} else if(frm.doc.payment_type == "Receive") {
if(tax.currency != frm.doc.paid_from_account_currency) {
//total_taxes_and_charges has the target currency. so using source conversion rate
frm.doc.total_taxes_and_charges += flt(current_tax_amount / frm.doc.source_exchange_rate);
} else {
frm.doc.total_taxes_and_charges += current_tax_amount;
}
}
frm.doc.base_total_taxes_and_charges += tax.base_tax_amount;
frm.refresh_field('taxes');
frm.refresh_field('total_taxes_and_charges');

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,7 @@
import unittest
import frappe
from frappe import qb
from frappe.utils import flt, nowdate
from erpnext.accounts.doctype.payment_entry.payment_entry import (
@@ -32,10 +33,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 +48,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 +59,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 +101,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 +119,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 +132,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 +151,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 +164,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 +192,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 +229,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 +259,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 +280,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 +308,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 +422,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 +444,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 +503,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 +529,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 +548,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 +559,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 +597,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 +613,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 +630,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 +650,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 +669,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 +690,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")
@@ -634,94 +724,164 @@ class TestPaymentEntry(unittest.TestCase):
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 = 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))
self.assertEqual(
flt(payment_entry.total_taxes_and_charges, 2), flt(10 / payment_entry.target_exchange_rate, 2)
)
def test_gl_of_multi_currency_payment_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": 100,
"add_deduct_tax": "Add",
"description": "Test",
},
)
payment_entry.target_exchange_rate = 80
payment_entry.received_amount = 12.5
payment_entry = payment_entry.submit()
gle = qb.DocType("GL Entry")
gl_entries = (
qb.from_(gle)
.select(
gle.account,
gle.debit,
gle.credit,
gle.debit_in_account_currency,
gle.credit_in_account_currency,
)
.orderby(gle.account)
.where(gle.voucher_no == payment_entry.name)
.run()
)
expected_gl_entries = (
("_Test Account Service Tax - _TC", 100.0, 0.0, 100.0, 0.0),
("_Test Bank - _TC", 0.0, 1100.0, 0.0, 1100.0),
("_Test Payable USD - _TC", 1000.0, 0.0, 12.5, 0),
)
self.assertEqual(gl_entries, expected_gl_entries)
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 = 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_no = "Test001"
payment_entry.reference_date = nowdate()
if args.get('save'):
if args.get("save"):
payment_entry.save()
if args.get('submit'):
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()

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