Merge branch 'develop' into fix_by_voucher_order_develop
This commit is contained in:
		
						commit
						85e2fd965a
					
				| @ -7,8 +7,8 @@ context('Form', () => { | |||||||
| 	it('create a new opportunity', () => { | 	it('create a new opportunity', () => { | ||||||
| 		cy.visit('/desk#Form/Opportunity/New Opportunity 1'); | 		cy.visit('/desk#Form/Opportunity/New Opportunity 1'); | ||||||
| 		cy.get('.page-title').should('contain', 'Not Saved'); | 		cy.get('.page-title').should('contain', 'Not Saved'); | ||||||
| 		cy.fill_field('enquiry_from', 'Customer', 'Select'); | 		cy.fill_field('opportunity_from', 'Customer', 'Select'); | ||||||
| 		cy.fill_field('customer', 'Test Customer', 'Link').blur(); | 		cy.fill_field('party_name', 'Test Customer', 'Link').blur(); | ||||||
| 		cy.get('.primary-action').click(); | 		cy.get('.primary-action').click(); | ||||||
| 		cy.get('.page-title').should('contain', 'Open'); | 		cy.get('.page-title').should('contain', 'Open'); | ||||||
| 		cy.get('.form-inner-toolbar button:contains("Lost")').click({ force: true }); | 		cy.get('.form-inner-toolbar button:contains("Lost")').click({ force: true }); | ||||||
| @ -29,4 +29,3 @@ context('Form', () => { | |||||||
| 		cy.get('.page-title').should('contain', 'Lost'); | 		cy.get('.page-title').should('contain', 'Lost'); | ||||||
| 	}); | 	}); | ||||||
| }); | }); | ||||||
| 
 |  | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ import frappe | |||||||
| from erpnext.hooks import regional_overrides | from erpnext.hooks import regional_overrides | ||||||
| from frappe.utils import getdate | from frappe.utils import getdate | ||||||
| 
 | 
 | ||||||
| __version__ = '11.1.20' | __version__ = '11.1.39' | ||||||
| 
 | 
 | ||||||
| def get_default_company(user=None): | def get_default_company(user=None): | ||||||
| 	'''Get default company for user''' | 	'''Get default company for user''' | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| { | { | ||||||
|     "country_code": "de",  |     "country_code": "de",  | ||||||
|     "name": "Germany - Kontenplan SKR04",  |     "name": "SKR04 ohne Kontonummern",  | ||||||
|     "tree": { |     "tree": { | ||||||
|         "Bilanz - Aktiva": { |         "Bilanz - Aktiva": { | ||||||
|             "Anlageverm\u00f6gen": { |             "Anlageverm\u00f6gen": { | ||||||
| @ -1383,8 +1383,7 @@ | |||||||
|                     "Sonstige Zinsen und \u00e4hnliche Ertr\u00e4ge 1": { |                     "Sonstige Zinsen und \u00e4hnliche Ertr\u00e4ge 1": { | ||||||
|                         "Diskontertr\u00e4ge": {},  |                         "Diskontertr\u00e4ge": {},  | ||||||
|                         "Diskontertr\u00e4ge aus verbundenen Unternehmen": {},  |                         "Diskontertr\u00e4ge aus verbundenen Unternehmen": {},  | ||||||
|                         "Laufende Ertr\u00e4ge aus Anteilen an Kapitalgesellschaften 100% / 50% steuerfrei": {},  |                         "Laufende Ertr\u00e4ge aus Anteilen an Kapitalgesellschaften 100% / 50% steuerfrei": {}, | ||||||
|                         "Laufende Ertr\u00e4ge aus Anteilen an Kapitalgesellschaften 100% / 50% steuerfrei": {},  |  | ||||||
|                         "Sonstige Zinsen und \u00e4hnliche Ertr\u00e4ge 2": {},  |                         "Sonstige Zinsen und \u00e4hnliche Ertr\u00e4ge 2": {},  | ||||||
|                         "Sonstige Zinsen und \u00e4hnliche Ertr\u00e4ge aus verbundenen Unternehmen": {},  |                         "Sonstige Zinsen und \u00e4hnliche Ertr\u00e4ge aus verbundenen Unternehmen": {},  | ||||||
|                         "Sonstige Zinsertr\u00e4ge": {},  |                         "Sonstige Zinsertr\u00e4ge": {},  | ||||||
| @ -1703,4 +1702,4 @@ | |||||||
|             "root_type": "Asset" |             "root_type": "Asset" | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -38,24 +38,24 @@ | |||||||
|                 "Kas": { |                 "Kas": { | ||||||
|                     "Kas Mata Uang Lain": { |                     "Kas Mata Uang Lain": { | ||||||
|                         "Kas USD": { |                         "Kas USD": { | ||||||
|                             "account_number": "1112.0010",  |                             "account_number": "1112.001",  | ||||||
|                             "account_type": "Cash" |                             "account_type": "Cash" | ||||||
|                         },  |                         },  | ||||||
|                         "account_number": "1112.000" |                         "account_number": "1112.000" | ||||||
|                     },  |                     },  | ||||||
|                     "Kas Rupiah": { |                     "Kas Rupiah": { | ||||||
|                         "Kas Besar": { |                         "Kas Besar": { | ||||||
|                             "account_number": "1111.0020",  |                             "account_number": "1111.002",  | ||||||
|                             "account_type": "Cash" |                             "account_type": "Cash" | ||||||
|                         },  |                         },  | ||||||
|                         "Kas Kecil": { |                         "Kas Kecil": { | ||||||
|                             "account_number": "1111.0010",  |                             "account_number": "1111.001",  | ||||||
|                             "account_type": "Cash" |                             "account_type": "Cash" | ||||||
|                         },  |                         },  | ||||||
|                         "account_number": "1111.000",  |                         "account_number": "1111.000",  | ||||||
|                         "account_type": "Cash" |                         "account_type": "Cash" | ||||||
|                     },  |                     },  | ||||||
|                     "account_number": "1110.0000" |                     "account_number": "1110.000" | ||||||
|                 },  |                 },  | ||||||
|                 "Pendapatan Yang Akan di Terima": { |                 "Pendapatan Yang Akan di Terima": { | ||||||
|                     "Pendapatan Yang di Terima": { |                     "Pendapatan Yang di Terima": { | ||||||
| @ -98,7 +98,7 @@ | |||||||
|                     },  |                     },  | ||||||
|                     "account_number": "1130.000" |                     "account_number": "1130.000" | ||||||
|                 },  |                 },  | ||||||
|                 "account_number": "1100.0000" |                 "account_number": "1100.000" | ||||||
|             },  |             },  | ||||||
|             "Aktiva Tetap": { |             "Aktiva Tetap": { | ||||||
|                 "Aktiva": { |                 "Aktiva": { | ||||||
| @ -121,20 +121,20 @@ | |||||||
|                 "Investasi": { |                 "Investasi": { | ||||||
|                     "Investasi": { |                     "Investasi": { | ||||||
|                         "Deposito": { |                         "Deposito": { | ||||||
|                             "account_number": "1231.003",  |                             "account_number": "1231.300",  | ||||||
|                             "is_group": 1 |                             "is_group": 1 | ||||||
|                         },  |                         },  | ||||||
|                         "Investai Saham": { |                         "Investasi Saham": { | ||||||
|                             "Investasi Saham": { |                             "Investasi Saham": { | ||||||
|                                 "account_number": "1231.0011" |                                 "account_number": "1231.101" | ||||||
|                             },  |                             },  | ||||||
|                             "account_number": "1231.001" |                             "account_number": "1231.100" | ||||||
|                         },  |                         },  | ||||||
|                         "Investasi Perumahan": { |                         "Investasi Perumahan": { | ||||||
|                             "Investasi Perumahan": { |                             "Investasi Perumahan": { | ||||||
|                                 "account_number": "1231.0021" |                                 "account_number": "1231.201" | ||||||
|                             },  |                             },  | ||||||
|                             "account_number": "1231.002" |                             "account_number": "1231.200" | ||||||
|                         },  |                         },  | ||||||
|                         "account_number": "1231.000" |                         "account_number": "1231.000" | ||||||
|                     },  |                     },  | ||||||
| @ -142,7 +142,7 @@ | |||||||
|                 },  |                 },  | ||||||
|                 "account_number": "1200.000" |                 "account_number": "1200.000" | ||||||
|             },  |             },  | ||||||
|             "account_number": "1000.0000",  |             "account_number": "1000.000",  | ||||||
|             "root_type": "Asset" |             "root_type": "Asset" | ||||||
|         },  |         },  | ||||||
|         "Beban": { |         "Beban": { | ||||||
| @ -684,4 +684,4 @@ | |||||||
|             "root_type": "Income" |             "root_type": "Income" | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -0,0 +1,8 @@ | |||||||
|  | // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 | ||||||
|  | // For license information, please see license.txt
 | ||||||
|  | 
 | ||||||
|  | frappe.ui.form.on('Account Subtype', { | ||||||
|  | 	refresh: function() { | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | }); | ||||||
							
								
								
									
										134
									
								
								erpnext/accounts/doctype/account_subtype/account_subtype.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								erpnext/accounts/doctype/account_subtype/account_subtype.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | |||||||
|  | { | ||||||
|  |  "allow_copy": 0,  | ||||||
|  |  "allow_events_in_timeline": 0,  | ||||||
|  |  "allow_guest_to_view": 0,  | ||||||
|  |  "allow_import": 1,  | ||||||
|  |  "allow_rename": 1,  | ||||||
|  |  "autoname": "field:account_subtype",  | ||||||
|  |  "beta": 0,  | ||||||
|  |  "creation": "2018-10-25 15:46:08.054586",  | ||||||
|  |  "custom": 0,  | ||||||
|  |  "docstatus": 0,  | ||||||
|  |  "doctype": "DocType",  | ||||||
|  |  "document_type": "",  | ||||||
|  |  "editable_grid": 1,  | ||||||
|  |  "engine": "InnoDB",  | ||||||
|  |  "fields": [ | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0,  | ||||||
|  |    "allow_in_quick_entry": 0,  | ||||||
|  |    "allow_on_submit": 0,  | ||||||
|  |    "bold": 0,  | ||||||
|  |    "collapsible": 0,  | ||||||
|  |    "columns": 0,  | ||||||
|  |    "fieldname": "account_subtype",  | ||||||
|  |    "fieldtype": "Data",  | ||||||
|  |    "hidden": 0,  | ||||||
|  |    "ignore_user_permissions": 0,  | ||||||
|  |    "ignore_xss_filter": 0,  | ||||||
|  |    "in_filter": 0,  | ||||||
|  |    "in_global_search": 0,  | ||||||
|  |    "in_list_view": 0,  | ||||||
|  |    "in_standard_filter": 0,  | ||||||
|  |    "label": "Account Subtype",  | ||||||
|  |    "length": 0,  | ||||||
|  |    "no_copy": 0,  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "precision": "",  | ||||||
|  |    "print_hide": 0,  | ||||||
|  |    "print_hide_if_no_value": 0,  | ||||||
|  |    "read_only": 0,  | ||||||
|  |    "remember_last_selected_value": 0,  | ||||||
|  |    "report_hide": 0,  | ||||||
|  |    "reqd": 0,  | ||||||
|  |    "search_index": 0,  | ||||||
|  |    "set_only_once": 0,  | ||||||
|  |    "translatable": 0,  | ||||||
|  |    "unique": 1 | ||||||
|  |   } | ||||||
|  |  ],  | ||||||
|  |  "has_web_view": 0,  | ||||||
|  |  "hide_heading": 0,  | ||||||
|  |  "hide_toolbar": 0,  | ||||||
|  |  "idx": 0,  | ||||||
|  |  "image_view": 0,  | ||||||
|  |  "in_create": 0,  | ||||||
|  |  "is_submittable": 0,  | ||||||
|  |  "issingle": 0,  | ||||||
|  |  "istable": 0,  | ||||||
|  |  "max_attachments": 0,  | ||||||
|  |  "modified": "2018-10-25 15:47:03.841390",  | ||||||
|  |  "modified_by": "Administrator",  | ||||||
|  |  "module": "Accounts",  | ||||||
|  |  "name": "Account Subtype",  | ||||||
|  |  "name_case": "",  | ||||||
|  |  "owner": "Administrator",  | ||||||
|  |  "permissions": [ | ||||||
|  |   { | ||||||
|  |    "amend": 0,  | ||||||
|  |    "cancel": 0,  | ||||||
|  |    "create": 1,  | ||||||
|  |    "delete": 1,  | ||||||
|  |    "email": 1,  | ||||||
|  |    "export": 1,  | ||||||
|  |    "if_owner": 0,  | ||||||
|  |    "import": 0,  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "print": 1,  | ||||||
|  |    "read": 1,  | ||||||
|  |    "report": 1,  | ||||||
|  |    "role": "System Manager",  | ||||||
|  |    "set_user_permissions": 0,  | ||||||
|  |    "share": 1,  | ||||||
|  |    "submit": 0,  | ||||||
|  |    "write": 1 | ||||||
|  |   },  | ||||||
|  |   { | ||||||
|  |    "amend": 0,  | ||||||
|  |    "cancel": 0,  | ||||||
|  |    "create": 1,  | ||||||
|  |    "delete": 1,  | ||||||
|  |    "email": 1,  | ||||||
|  |    "export": 1,  | ||||||
|  |    "if_owner": 0,  | ||||||
|  |    "import": 0,  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "print": 1,  | ||||||
|  |    "read": 1,  | ||||||
|  |    "report": 1,  | ||||||
|  |    "role": "Accounts Manager",  | ||||||
|  |    "set_user_permissions": 0,  | ||||||
|  |    "share": 1,  | ||||||
|  |    "submit": 0,  | ||||||
|  |    "write": 1 | ||||||
|  |   },  | ||||||
|  |   { | ||||||
|  |    "amend": 0,  | ||||||
|  |    "cancel": 0,  | ||||||
|  |    "create": 1,  | ||||||
|  |    "delete": 1,  | ||||||
|  |    "email": 1,  | ||||||
|  |    "export": 1,  | ||||||
|  |    "if_owner": 0,  | ||||||
|  |    "import": 0,  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "print": 1,  | ||||||
|  |    "read": 1,  | ||||||
|  |    "report": 1,  | ||||||
|  |    "role": "Accounts User",  | ||||||
|  |    "set_user_permissions": 0,  | ||||||
|  |    "share": 1,  | ||||||
|  |    "submit": 0,  | ||||||
|  |    "write": 1 | ||||||
|  |   } | ||||||
|  |  ],  | ||||||
|  |  "quick_entry": 1,  | ||||||
|  |  "read_only": 0,  | ||||||
|  |  "read_only_onload": 0,  | ||||||
|  |  "show_name_in_global_search": 0,  | ||||||
|  |  "sort_field": "modified",  | ||||||
|  |  "sort_order": "DESC",  | ||||||
|  |  "track_changes": 0,  | ||||||
|  |  "track_seen": 0,  | ||||||
|  |  "track_views": 0 | ||||||
|  | } | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors | ||||||
|  | # For license information, please see license.txt | ||||||
|  | 
 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | from frappe.model.document import Document | ||||||
|  | 
 | ||||||
|  | class AccountSubtype(Document): | ||||||
|  | 	pass | ||||||
| @ -0,0 +1,23 @@ | |||||||
|  | /* eslint-disable */ | ||||||
|  | // rename this file from _test_[name] to test_[name] to activate
 | ||||||
|  | // and remove above this line
 | ||||||
|  | 
 | ||||||
|  | QUnit.test("test: Account Subtype", function (assert) { | ||||||
|  | 	let done = assert.async(); | ||||||
|  | 
 | ||||||
|  | 	// number of asserts
 | ||||||
|  | 	assert.expect(1); | ||||||
|  | 
 | ||||||
|  | 	frappe.run_serially([ | ||||||
|  | 		// insert a new Account Subtype
 | ||||||
|  | 		() => frappe.tests.make('Account Subtype', [ | ||||||
|  | 			// values to be set
 | ||||||
|  | 			{key: 'value'} | ||||||
|  | 		]), | ||||||
|  | 		() => { | ||||||
|  | 			assert.equal(cur_frm.doc.key, 'value'); | ||||||
|  | 		}, | ||||||
|  | 		() => done() | ||||||
|  | 	]); | ||||||
|  | 
 | ||||||
|  | }); | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors | ||||||
|  | # See license.txt | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | class TestAccountSubtype(unittest.TestCase): | ||||||
|  | 	pass | ||||||
							
								
								
									
										0
									
								
								erpnext/accounts/doctype/account_type/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								erpnext/accounts/doctype/account_type/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										8
									
								
								erpnext/accounts/doctype/account_type/account_type.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								erpnext/accounts/doctype/account_type/account_type.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,8 @@ | |||||||
|  | // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 | ||||||
|  | // For license information, please see license.txt
 | ||||||
|  | 
 | ||||||
|  | frappe.ui.form.on('Account Type', { | ||||||
|  | 	refresh: function() { | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | }); | ||||||
							
								
								
									
										134
									
								
								erpnext/accounts/doctype/account_type/account_type.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								erpnext/accounts/doctype/account_type/account_type.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,134 @@ | |||||||
|  | { | ||||||
|  |  "allow_copy": 0,  | ||||||
|  |  "allow_events_in_timeline": 0,  | ||||||
|  |  "allow_guest_to_view": 0,  | ||||||
|  |  "allow_import": 1,  | ||||||
|  |  "allow_rename": 1,  | ||||||
|  |  "autoname": "field:account_type",  | ||||||
|  |  "beta": 0,  | ||||||
|  |  "creation": "2018-10-25 15:45:45.789963",  | ||||||
|  |  "custom": 0,  | ||||||
|  |  "docstatus": 0,  | ||||||
|  |  "doctype": "DocType",  | ||||||
|  |  "document_type": "",  | ||||||
|  |  "editable_grid": 1,  | ||||||
|  |  "engine": "InnoDB",  | ||||||
|  |  "fields": [ | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0,  | ||||||
|  |    "allow_in_quick_entry": 0,  | ||||||
|  |    "allow_on_submit": 0,  | ||||||
|  |    "bold": 0,  | ||||||
|  |    "collapsible": 0,  | ||||||
|  |    "columns": 0,  | ||||||
|  |    "fieldname": "account_type",  | ||||||
|  |    "fieldtype": "Data",  | ||||||
|  |    "hidden": 0,  | ||||||
|  |    "ignore_user_permissions": 0,  | ||||||
|  |    "ignore_xss_filter": 0,  | ||||||
|  |    "in_filter": 0,  | ||||||
|  |    "in_global_search": 0,  | ||||||
|  |    "in_list_view": 0,  | ||||||
|  |    "in_standard_filter": 0,  | ||||||
|  |    "label": "Account Type",  | ||||||
|  |    "length": 0,  | ||||||
|  |    "no_copy": 0,  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "precision": "",  | ||||||
|  |    "print_hide": 0,  | ||||||
|  |    "print_hide_if_no_value": 0,  | ||||||
|  |    "read_only": 0,  | ||||||
|  |    "remember_last_selected_value": 0,  | ||||||
|  |    "report_hide": 0,  | ||||||
|  |    "reqd": 0,  | ||||||
|  |    "search_index": 0,  | ||||||
|  |    "set_only_once": 0,  | ||||||
|  |    "translatable": 0,  | ||||||
|  |    "unique": 1 | ||||||
|  |   } | ||||||
|  |  ],  | ||||||
|  |  "has_web_view": 0,  | ||||||
|  |  "hide_heading": 0,  | ||||||
|  |  "hide_toolbar": 0,  | ||||||
|  |  "idx": 0,  | ||||||
|  |  "image_view": 0,  | ||||||
|  |  "in_create": 0,  | ||||||
|  |  "is_submittable": 0,  | ||||||
|  |  "issingle": 0,  | ||||||
|  |  "istable": 0,  | ||||||
|  |  "max_attachments": 0,  | ||||||
|  |  "modified": "2018-10-25 15:46:51.042604",  | ||||||
|  |  "modified_by": "Administrator",  | ||||||
|  |  "module": "Accounts",  | ||||||
|  |  "name": "Account Type",  | ||||||
|  |  "name_case": "",  | ||||||
|  |  "owner": "Administrator",  | ||||||
|  |  "permissions": [ | ||||||
|  |   { | ||||||
|  |    "amend": 0,  | ||||||
|  |    "cancel": 0,  | ||||||
|  |    "create": 1,  | ||||||
|  |    "delete": 1,  | ||||||
|  |    "email": 1,  | ||||||
|  |    "export": 1,  | ||||||
|  |    "if_owner": 0,  | ||||||
|  |    "import": 0,  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "print": 1,  | ||||||
|  |    "read": 1,  | ||||||
|  |    "report": 1,  | ||||||
|  |    "role": "System Manager",  | ||||||
|  |    "set_user_permissions": 0,  | ||||||
|  |    "share": 1,  | ||||||
|  |    "submit": 0,  | ||||||
|  |    "write": 1 | ||||||
|  |   },  | ||||||
|  |   { | ||||||
|  |    "amend": 0,  | ||||||
|  |    "cancel": 0,  | ||||||
|  |    "create": 1,  | ||||||
|  |    "delete": 1,  | ||||||
|  |    "email": 1,  | ||||||
|  |    "export": 1,  | ||||||
|  |    "if_owner": 0,  | ||||||
|  |    "import": 0,  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "print": 1,  | ||||||
|  |    "read": 1,  | ||||||
|  |    "report": 1,  | ||||||
|  |    "role": "Accounts Manager",  | ||||||
|  |    "set_user_permissions": 0,  | ||||||
|  |    "share": 1,  | ||||||
|  |    "submit": 0,  | ||||||
|  |    "write": 1 | ||||||
|  |   },  | ||||||
|  |   { | ||||||
|  |    "amend": 0,  | ||||||
|  |    "cancel": 0,  | ||||||
|  |    "create": 1,  | ||||||
|  |    "delete": 1,  | ||||||
|  |    "email": 1,  | ||||||
|  |    "export": 1,  | ||||||
|  |    "if_owner": 0,  | ||||||
|  |    "import": 0,  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "print": 1,  | ||||||
|  |    "read": 1,  | ||||||
|  |    "report": 1,  | ||||||
|  |    "role": "Accounts User",  | ||||||
|  |    "set_user_permissions": 0,  | ||||||
|  |    "share": 1,  | ||||||
|  |    "submit": 0,  | ||||||
|  |    "write": 1 | ||||||
|  |   } | ||||||
|  |  ],  | ||||||
|  |  "quick_entry": 1,  | ||||||
|  |  "read_only": 0,  | ||||||
|  |  "read_only_onload": 0,  | ||||||
|  |  "show_name_in_global_search": 0,  | ||||||
|  |  "sort_field": "modified",  | ||||||
|  |  "sort_order": "DESC",  | ||||||
|  |  "track_changes": 0,  | ||||||
|  |  "track_seen": 0,  | ||||||
|  |  "track_views": 0 | ||||||
|  | } | ||||||
							
								
								
									
										9
									
								
								erpnext/accounts/doctype/account_type/account_type.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								erpnext/accounts/doctype/account_type/account_type.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors | ||||||
|  | # For license information, please see license.txt | ||||||
|  | 
 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | from frappe.model.document import Document | ||||||
|  | 
 | ||||||
|  | class AccountType(Document): | ||||||
|  | 	pass | ||||||
							
								
								
									
										23
									
								
								erpnext/accounts/doctype/account_type/test_account_type.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								erpnext/accounts/doctype/account_type/test_account_type.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | /* eslint-disable */ | ||||||
|  | // rename this file from _test_[name] to test_[name] to activate
 | ||||||
|  | // and remove above this line
 | ||||||
|  | 
 | ||||||
|  | QUnit.test("test: Account Type", function (assert) { | ||||||
|  | 	let done = assert.async(); | ||||||
|  | 
 | ||||||
|  | 	// number of asserts
 | ||||||
|  | 	assert.expect(1); | ||||||
|  | 
 | ||||||
|  | 	frappe.run_serially([ | ||||||
|  | 		// insert a new Account Type
 | ||||||
|  | 		() => frappe.tests.make('Account Type', [ | ||||||
|  | 			// values to be set
 | ||||||
|  | 			{key: 'value'} | ||||||
|  | 		]), | ||||||
|  | 		() => { | ||||||
|  | 			assert.equal(cur_frm.doc.key, 'value'); | ||||||
|  | 		}, | ||||||
|  | 		() => done() | ||||||
|  | 	]); | ||||||
|  | 
 | ||||||
|  | }); | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors | ||||||
|  | # See license.txt | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | import unittest | ||||||
|  | 
 | ||||||
|  | class TestAccountType(unittest.TestCase): | ||||||
|  | 	pass | ||||||
| @ -5,6 +5,7 @@ | |||||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||||
| import frappe | import frappe | ||||||
| from frappe.model.document import Document | from frappe.model.document import Document | ||||||
|  | from frappe import _ | ||||||
| 
 | 
 | ||||||
| class AccountingPeriod(Document): | class AccountingPeriod(Document): | ||||||
| 	def validate(self): | 	def validate(self): | ||||||
| @ -16,7 +17,7 @@ class AccountingPeriod(Document): | |||||||
| 	def autoname(self): | 	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]) | 		self.name = " - ".join([self.period_name, company_abbr]) | ||||||
| 	 | 
 | ||||||
| 	def validate_overlap(self): | 	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 ( | 			where ( | ||||||
| @ -33,7 +34,7 @@ class AccountingPeriod(Document): | |||||||
| 			}, as_dict=True) | 			}, as_dict=True) | ||||||
| 
 | 
 | ||||||
| 		if len(existing_accounting_period) > 0: | 		if len(existing_accounting_period) > 0: | ||||||
| 			frappe.throw("Accounting Period overlaps with {0}".format(existing_accounting_period[0].get("name"))) | 			frappe.throw(_("Accounting Period overlaps with {0}".format(existing_accounting_period[0].get("name")))) | ||||||
| 
 | 
 | ||||||
| 	def get_doctypes_for_closing(self): | 	def get_doctypes_for_closing(self): | ||||||
| 		docs_for_closing = [] | 		docs_for_closing = [] | ||||||
|  | |||||||
| @ -1,203 +1,203 @@ | |||||||
| { | { | ||||||
|  "creation": "2013-06-24 15:49:57", |    "creation": "2013-06-24 15:49:57", | ||||||
|  "description": "Settings for Accounts", |    "description": "Settings for Accounts", | ||||||
|  "doctype": "DocType", |    "doctype": "DocType", | ||||||
|  "document_type": "Other", |    "document_type": "Other", | ||||||
|  "editable_grid": 1, |    "editable_grid": 1, | ||||||
|  "engine": "InnoDB", |    "engine": "InnoDB", | ||||||
|  "field_order": [ |    "field_order": [ | ||||||
|   "auto_accounting_for_stock", |     "auto_accounting_for_stock", | ||||||
|   "acc_frozen_upto", |     "acc_frozen_upto", | ||||||
|   "frozen_accounts_modifier", |     "frozen_accounts_modifier", | ||||||
|   "determine_address_tax_category_from", |     "determine_address_tax_category_from", | ||||||
|   "column_break_4", |     "column_break_4", | ||||||
|   "credit_controller", |     "credit_controller", | ||||||
|   "check_supplier_invoice_uniqueness", |     "check_supplier_invoice_uniqueness", | ||||||
|   "make_payment_via_journal_entry", |     "make_payment_via_journal_entry", | ||||||
|   "unlink_payment_on_cancellation_of_invoice", |     "unlink_payment_on_cancellation_of_invoice", | ||||||
|   "unlink_advance_payment_on_cancelation_of_order", |     "unlink_advance_payment_on_cancelation_of_order", | ||||||
|   "book_asset_depreciation_entry_automatically", |     "book_asset_depreciation_entry_automatically", | ||||||
|   "allow_cost_center_in_entry_of_bs_account", |     "allow_cost_center_in_entry_of_bs_account", | ||||||
|   "add_taxes_from_item_tax_template", |     "add_taxes_from_item_tax_template", | ||||||
|   "automatically_fetch_payment_terms", |     "automatically_fetch_payment_terms", | ||||||
|   "print_settings", |     "print_settings", | ||||||
|   "show_inclusive_tax_in_print", |     "show_inclusive_tax_in_print", | ||||||
|   "column_break_12", |     "column_break_12", | ||||||
|   "show_payment_schedule_in_print", |     "show_payment_schedule_in_print", | ||||||
|   "currency_exchange_section", |     "currency_exchange_section", | ||||||
|   "allow_stale", |     "allow_stale", | ||||||
|   "stale_days", |     "stale_days", | ||||||
|   "report_settings_sb", |     "report_settings_sb", | ||||||
|   "use_custom_cash_flow" |     "use_custom_cash_flow" | ||||||
|  ], |    ], | ||||||
|  "fields": [ |    "fields": [ | ||||||
|   { |     { | ||||||
|    "default": "1", |      "default": "1", | ||||||
|    "description": "If enabled, the system will post accounting entries for inventory automatically.", |      "description": "If enabled, the system will post accounting entries for inventory automatically.", | ||||||
|    "fieldname": "auto_accounting_for_stock", |      "fieldname": "auto_accounting_for_stock", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "hidden": 1, |      "hidden": 1, | ||||||
|    "in_list_view": 1, |      "in_list_view": 1, | ||||||
|    "label": "Make Accounting Entry For Every Stock Movement" |      "label": "Make Accounting Entry For Every Stock Movement" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "description": "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.", |      "description": "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.", | ||||||
|    "fieldname": "acc_frozen_upto", |      "fieldname": "acc_frozen_upto", | ||||||
|    "fieldtype": "Date", |      "fieldtype": "Date", | ||||||
|    "in_list_view": 1, |      "in_list_view": 1, | ||||||
|    "label": "Accounts Frozen Upto" |      "label": "Accounts Frozen Upto" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "description": "Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts", |      "description": "Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts", | ||||||
|    "fieldname": "frozen_accounts_modifier", |      "fieldname": "frozen_accounts_modifier", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "in_list_view": 1, |      "in_list_view": 1, | ||||||
|    "label": "Role Allowed to Set Frozen Accounts & Edit Frozen Entries", |      "label": "Role Allowed to Set Frozen Accounts & Edit Frozen Entries", | ||||||
|    "options": "Role" |      "options": "Role" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "default": "Billing Address", |      "default": "Billing Address", | ||||||
|    "description": "Address used to determine Tax Category in transactions.", |      "description": "Address used to determine Tax Category in transactions.", | ||||||
|    "fieldname": "determine_address_tax_category_from", |      "fieldname": "determine_address_tax_category_from", | ||||||
|    "fieldtype": "Select", |      "fieldtype": "Select", | ||||||
|    "label": "Determine Address Tax Category From", |      "label": "Determine Address Tax Category From", | ||||||
|    "options": "Billing Address\nShipping Address" |      "options": "Billing Address\nShipping Address" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "column_break_4", |      "fieldname": "column_break_4", | ||||||
|    "fieldtype": "Column Break" |      "fieldtype": "Column Break" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "description": "Role that is allowed to submit transactions that exceed credit limits set.", |      "description": "Role that is allowed to submit transactions that exceed credit limits set.", | ||||||
|    "fieldname": "credit_controller", |      "fieldname": "credit_controller", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "in_list_view": 1, |      "in_list_view": 1, | ||||||
|    "label": "Credit Controller", |      "label": "Credit Controller", | ||||||
|    "options": "Role" |      "options": "Role" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "check_supplier_invoice_uniqueness", |      "fieldname": "check_supplier_invoice_uniqueness", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Check Supplier Invoice Number Uniqueness" |      "label": "Check Supplier Invoice Number Uniqueness" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "make_payment_via_journal_entry", |      "fieldname": "make_payment_via_journal_entry", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Make Payment via Journal Entry" |      "label": "Make Payment via Journal Entry" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "default": "1", |      "default": "1", | ||||||
|    "fieldname": "unlink_payment_on_cancellation_of_invoice", |      "fieldname": "unlink_payment_on_cancellation_of_invoice", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Unlink Payment on Cancellation of Invoice" |      "label": "Unlink Payment on Cancellation of Invoice" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "default": "1", |      "default": "1", | ||||||
|    "fieldname": "unlink_advance_payment_on_cancelation_of_order", |      "fieldname": "unlink_advance_payment_on_cancelation_of_order", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Unlink Advance Payment on Cancelation of Order" |      "label": "Unlink Advance Payment on Cancelation of Order" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "default": "1", |      "default": "1", | ||||||
|    "fieldname": "book_asset_depreciation_entry_automatically", |      "fieldname": "book_asset_depreciation_entry_automatically", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Book Asset Depreciation Entry Automatically" |      "label": "Book Asset Depreciation Entry Automatically" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "allow_cost_center_in_entry_of_bs_account", |      "fieldname": "allow_cost_center_in_entry_of_bs_account", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Allow Cost Center In Entry of Balance Sheet Account" |      "label": "Allow Cost Center In Entry of Balance Sheet Account" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "default": "1", |      "default": "1", | ||||||
|    "fieldname": "add_taxes_from_item_tax_template", |      "fieldname": "add_taxes_from_item_tax_template", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Automatically Add Taxes and Charges from Item Tax Template" |      "label": "Automatically Add Taxes and Charges from Item Tax Template" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "print_settings", |      "fieldname": "print_settings", | ||||||
|    "fieldtype": "Section Break", |      "fieldtype": "Section Break", | ||||||
|    "label": "Print Settings" |      "label": "Print Settings" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "show_inclusive_tax_in_print", |      "fieldname": "show_inclusive_tax_in_print", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Show Inclusive Tax In Print" |      "label": "Show Inclusive Tax In Print" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "column_break_12", |      "fieldname": "column_break_12", | ||||||
|    "fieldtype": "Column Break" |      "fieldtype": "Column Break" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "show_payment_schedule_in_print", |      "fieldname": "show_payment_schedule_in_print", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Show Payment Schedule in Print" |      "label": "Show Payment Schedule in Print" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "currency_exchange_section", |      "fieldname": "currency_exchange_section", | ||||||
|    "fieldtype": "Section Break", |      "fieldtype": "Section Break", | ||||||
|    "label": "Currency Exchange Settings" |      "label": "Currency Exchange Settings" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "default": "1", |      "default": "1", | ||||||
|    "fieldname": "allow_stale", |      "fieldname": "allow_stale", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "in_list_view": 1, |      "in_list_view": 1, | ||||||
|    "label": "Allow Stale Exchange Rates" |      "label": "Allow Stale Exchange Rates" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "default": "1", |      "default": "1", | ||||||
|    "depends_on": "eval:doc.allow_stale==0", |      "depends_on": "eval:doc.allow_stale==0", | ||||||
|    "fieldname": "stale_days", |      "fieldname": "stale_days", | ||||||
|    "fieldtype": "Int", |      "fieldtype": "Int", | ||||||
|    "label": "Stale Days" |      "label": "Stale Days" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "report_settings_sb", |      "fieldname": "report_settings_sb", | ||||||
|    "fieldtype": "Section Break", |      "fieldtype": "Section Break", | ||||||
|    "label": "Report Settings" |      "label": "Report Settings" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "default": "0", |      "default": "0", | ||||||
|    "description": "Only select if you have setup Cash Flow Mapper documents", |      "description": "Only select if you have setup Cash Flow Mapper documents", | ||||||
|    "fieldname": "use_custom_cash_flow", |      "fieldname": "use_custom_cash_flow", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Use Custom Cash Flow Format" |      "label": "Use Custom Cash Flow Format" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "automatically_fetch_payment_terms", |      "fieldname": "automatically_fetch_payment_terms", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Automatically Fetch Payment Terms" |      "label": "Automatically Fetch Payment Terms" | ||||||
|   } |     } | ||||||
|  ], |    ], | ||||||
|  "icon": "icon-cog", |    "icon": "icon-cog", | ||||||
|  "idx": 1, |    "idx": 1, | ||||||
|  "issingle": 1, |    "issingle": 1, | ||||||
|  "modified": "2019-04-28 18:20:55.789946", |    "modified": "2019-04-28 18:20:55.789946", | ||||||
|  "modified_by": "Administrator", |    "modified_by": "Administrator", | ||||||
|  "module": "Accounts", |    "module": "Accounts", | ||||||
|  "name": "Accounts Settings", |    "name": "Accounts Settings", | ||||||
|  "owner": "Administrator", |    "owner": "Administrator", | ||||||
|  "permissions": [ |    "permissions": [ | ||||||
|   { |     { | ||||||
|    "create": 1, |      "create": 1, | ||||||
|    "email": 1, |      "email": 1, | ||||||
|    "print": 1, |      "print": 1, | ||||||
|    "read": 1, |      "read": 1, | ||||||
|    "role": "Accounts Manager", |      "role": "Accounts Manager", | ||||||
|    "share": 1, |      "share": 1, | ||||||
|    "write": 1 |      "write": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "read": 1, |      "read": 1, | ||||||
|    "role": "Sales User" |      "role": "Sales User" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "read": 1, |      "read": 1, | ||||||
|    "role": "Purchase User" |      "role": "Purchase User" | ||||||
|   } |     } | ||||||
|  ], |    ], | ||||||
|  "quick_entry": 1, |    "quick_entry": 1, | ||||||
|  "sort_order": "ASC", |    "sort_order": "ASC", | ||||||
|  "track_changes": 1 |    "track_changes": 1 | ||||||
| } |   } | ||||||
| @ -2,7 +2,29 @@ | |||||||
| // For license information, please see license.txt
 | // For license information, please see license.txt
 | ||||||
| 
 | 
 | ||||||
| frappe.ui.form.on('Bank', { | frappe.ui.form.on('Bank', { | ||||||
|  | 	onload: function(frm) { | ||||||
|  | 		add_fields_to_mapping_table(frm); | ||||||
|  | 	}, | ||||||
| 	refresh: function(frm) { | 	refresh: function(frm) { | ||||||
| 
 | 		add_fields_to_mapping_table(frm); | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | let add_fields_to_mapping_table = function (frm) { | ||||||
|  | 	let options = []; | ||||||
|  | 
 | ||||||
|  | 	frappe.model.with_doctype("Bank Transaction", function() { | ||||||
|  | 		let meta = frappe.get_meta("Bank Transaction"); | ||||||
|  | 		meta.fields.forEach(value => { | ||||||
|  | 			if (!["Section Break", "Column Break"].includes(value.fieldtype)) { | ||||||
|  | 				options.push(value.fieldname); | ||||||
|  | 			} | ||||||
|  | 		}); | ||||||
|  | 	}); | ||||||
|  | 
 | ||||||
|  | 	frappe.meta.get_docfield("Bank Transaction Mapping", "bank_transaction_field", | ||||||
|  | 		frm.doc.name).options = options; | ||||||
|  | 
 | ||||||
|  | 	frm.fields_dict.bank_transaction_mapping.grid.refresh(); | ||||||
|  | }; | ||||||
| @ -1,5 +1,6 @@ | |||||||
| { | { | ||||||
|  "allow_copy": 0,  |  "allow_copy": 0,  | ||||||
|  |  "allow_events_in_timeline": 0,  | ||||||
|  "allow_guest_to_view": 0,  |  "allow_guest_to_view": 0,  | ||||||
|  "allow_import": 0,  |  "allow_import": 0,  | ||||||
|  "allow_rename": 0,  |  "allow_rename": 0,  | ||||||
| @ -15,6 +16,7 @@ | |||||||
|  "fields": [ |  "fields": [ | ||||||
|   { |   { | ||||||
|    "allow_bulk_edit": 0,  |    "allow_bulk_edit": 0,  | ||||||
|  |    "allow_in_quick_entry": 0,  | ||||||
|    "allow_on_submit": 0,  |    "allow_on_submit": 0,  | ||||||
|    "bold": 0,  |    "bold": 0,  | ||||||
|    "collapsible": 0,  |    "collapsible": 0,  | ||||||
| @ -42,6 +44,134 @@ | |||||||
|    "search_index": 0,  |    "search_index": 0,  | ||||||
|    "set_only_once": 0,  |    "set_only_once": 0,  | ||||||
|    "translatable": 0,  |    "translatable": 0,  | ||||||
|  |    "unique": 1 | ||||||
|  |   },  | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0,  | ||||||
|  |    "allow_in_quick_entry": 0,  | ||||||
|  |    "allow_on_submit": 0,  | ||||||
|  |    "bold": 0,  | ||||||
|  |    "collapsible": 1,  | ||||||
|  |    "columns": 0,  | ||||||
|  |    "fieldname": "data_import_configuration_section",  | ||||||
|  |    "fieldtype": "Section Break",  | ||||||
|  |    "hidden": 0,  | ||||||
|  |    "ignore_user_permissions": 0,  | ||||||
|  |    "ignore_xss_filter": 0,  | ||||||
|  |    "in_filter": 0,  | ||||||
|  |    "in_global_search": 0,  | ||||||
|  |    "in_list_view": 0,  | ||||||
|  |    "in_standard_filter": 0,  | ||||||
|  |    "label": "Data Import Configuration",  | ||||||
|  |    "length": 0,  | ||||||
|  |    "no_copy": 0,  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "precision": "",  | ||||||
|  |    "print_hide": 0,  | ||||||
|  |    "print_hide_if_no_value": 0,  | ||||||
|  |    "read_only": 0,  | ||||||
|  |    "remember_last_selected_value": 0,  | ||||||
|  |    "report_hide": 0,  | ||||||
|  |    "reqd": 0,  | ||||||
|  |    "search_index": 0,  | ||||||
|  |    "set_only_once": 0,  | ||||||
|  |    "translatable": 0,  | ||||||
|  |    "unique": 0 | ||||||
|  |   },  | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0,  | ||||||
|  |    "allow_in_quick_entry": 0,  | ||||||
|  |    "allow_on_submit": 0,  | ||||||
|  |    "bold": 0,  | ||||||
|  |    "collapsible": 0,  | ||||||
|  |    "columns": 0,  | ||||||
|  |    "fieldname": "bank_transaction_mapping",  | ||||||
|  |    "fieldtype": "Table",  | ||||||
|  |    "hidden": 0,  | ||||||
|  |    "ignore_user_permissions": 0,  | ||||||
|  |    "ignore_xss_filter": 0,  | ||||||
|  |    "in_filter": 0,  | ||||||
|  |    "in_global_search": 0,  | ||||||
|  |    "in_list_view": 0,  | ||||||
|  |    "in_standard_filter": 0,  | ||||||
|  |    "label": "Bank Transaction Mapping",  | ||||||
|  |    "length": 0,  | ||||||
|  |    "no_copy": 0,  | ||||||
|  |    "options": "Bank Transaction Mapping",  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "precision": "",  | ||||||
|  |    "print_hide": 0,  | ||||||
|  |    "print_hide_if_no_value": 0,  | ||||||
|  |    "read_only": 0,  | ||||||
|  |    "remember_last_selected_value": 0,  | ||||||
|  |    "report_hide": 0,  | ||||||
|  |    "reqd": 0,  | ||||||
|  |    "search_index": 0,  | ||||||
|  |    "set_only_once": 0,  | ||||||
|  |    "translatable": 0,  | ||||||
|  |    "unique": 0 | ||||||
|  |   },  | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0,  | ||||||
|  |    "allow_in_quick_entry": 0,  | ||||||
|  |    "allow_on_submit": 0,  | ||||||
|  |    "bold": 0,  | ||||||
|  |    "collapsible": 0,  | ||||||
|  |    "columns": 0,  | ||||||
|  |    "fieldname": "section_break_4",  | ||||||
|  |    "fieldtype": "Section Break",  | ||||||
|  |    "hidden": 0,  | ||||||
|  |    "ignore_user_permissions": 0,  | ||||||
|  |    "ignore_xss_filter": 0,  | ||||||
|  |    "in_filter": 0,  | ||||||
|  |    "in_global_search": 0,  | ||||||
|  |    "in_list_view": 0,  | ||||||
|  |    "in_standard_filter": 0,  | ||||||
|  |    "length": 0,  | ||||||
|  |    "no_copy": 0,  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "precision": "",  | ||||||
|  |    "print_hide": 0,  | ||||||
|  |    "print_hide_if_no_value": 0,  | ||||||
|  |    "read_only": 0,  | ||||||
|  |    "remember_last_selected_value": 0,  | ||||||
|  |    "report_hide": 0,  | ||||||
|  |    "reqd": 0,  | ||||||
|  |    "search_index": 0,  | ||||||
|  |    "set_only_once": 0,  | ||||||
|  |    "translatable": 0,  | ||||||
|  |    "unique": 0 | ||||||
|  |   },  | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0,  | ||||||
|  |    "allow_in_quick_entry": 0,  | ||||||
|  |    "allow_on_submit": 0,  | ||||||
|  |    "bold": 0,  | ||||||
|  |    "collapsible": 0,  | ||||||
|  |    "columns": 0,  | ||||||
|  |    "fieldname": "plaid_access_token",  | ||||||
|  |    "fieldtype": "Data",  | ||||||
|  |    "hidden": 1,  | ||||||
|  |    "ignore_user_permissions": 0,  | ||||||
|  |    "ignore_xss_filter": 0,  | ||||||
|  |    "in_filter": 0,  | ||||||
|  |    "in_global_search": 0,  | ||||||
|  |    "in_list_view": 0,  | ||||||
|  |    "in_standard_filter": 0,  | ||||||
|  |    "label": "Plaid Access Token",  | ||||||
|  |    "length": 0,  | ||||||
|  |    "no_copy": 1,  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "precision": "",  | ||||||
|  |    "print_hide": 0,  | ||||||
|  |    "print_hide_if_no_value": 0,  | ||||||
|  |    "read_only": 1,  | ||||||
|  |    "remember_last_selected_value": 0,  | ||||||
|  |    "report_hide": 0,  | ||||||
|  |    "reqd": 0,  | ||||||
|  |    "search_index": 0,  | ||||||
|  |    "set_only_once": 0,  | ||||||
|  |    "translatable": 0,  | ||||||
|    "unique": 0 |    "unique": 0 | ||||||
|   } |   } | ||||||
|  ],  |  ],  | ||||||
| @ -55,7 +185,7 @@ | |||||||
|  "issingle": 0,  |  "issingle": 0,  | ||||||
|  "istable": 0,  |  "istable": 0,  | ||||||
|  "max_attachments": 0,  |  "max_attachments": 0,  | ||||||
|  "modified": "2018-04-07 17:00:21.246202",  |  "modified": "2018-11-27 16:12:13.938776",  | ||||||
|  "modified_by": "Administrator",  |  "modified_by": "Administrator",  | ||||||
|  "module": "Accounts",  |  "module": "Accounts",  | ||||||
|  "name": "Bank",  |  "name": "Bank",  | ||||||
| @ -64,7 +194,6 @@ | |||||||
|  "permissions": [ |  "permissions": [ | ||||||
|   { |   { | ||||||
|    "amend": 0,  |    "amend": 0,  | ||||||
|    "apply_user_permissions": 0,  |  | ||||||
|    "cancel": 0,  |    "cancel": 0,  | ||||||
|    "create": 1,  |    "create": 1,  | ||||||
|    "delete": 1,  |    "delete": 1,  | ||||||
| @ -90,5 +219,6 @@ | |||||||
|  "sort_field": "modified",  |  "sort_field": "modified",  | ||||||
|  "sort_order": "DESC",  |  "sort_order": "DESC",  | ||||||
|  "track_changes": 1,  |  "track_changes": 1,  | ||||||
|  "track_seen": 0 |  "track_seen": 0,  | ||||||
|  |  "track_views": 0 | ||||||
| } | } | ||||||
| @ -1,4 +1,4 @@ | |||||||
| // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
 | // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 | ||||||
| // For license information, please see license.txt
 | // For license information, please see license.txt
 | ||||||
| 
 | 
 | ||||||
| frappe.ui.form.on('Bank Account', { | frappe.ui.form.on('Bank Account', { | ||||||
| @ -29,5 +29,13 @@ frappe.ui.form.on('Bank Account', { | |||||||
| 		else { | 		else { | ||||||
| 			frappe.contacts.render_address_and_contact(frm); | 			frappe.contacts.render_address_and_contact(frm); | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 		if (frm.doc.integration_id) { | ||||||
|  | 			frm.add_custom_button(__("Unlink external integrations"), function() { | ||||||
|  | 				frappe.confirm(__("This action will unlink this account from any external service integrating ERPNext with your bank accounts. It cannot be undone. Are you certain ?"), function() { | ||||||
|  | 					frm.set_value("integration_id", ""); | ||||||
|  | 				}); | ||||||
|  | 			}); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| }); | }); | ||||||
|  | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -13,16 +13,47 @@ class BankAccount(Document): | |||||||
| 		"""Load address and contacts in `__onload`""" | 		"""Load address and contacts in `__onload`""" | ||||||
| 		load_address_and_contact(self) | 		load_address_and_contact(self) | ||||||
| 
 | 
 | ||||||
|  | 	def autoname(self): | ||||||
|  | 		self.name = self.account_name + " - " + self.bank | ||||||
|  | 
 | ||||||
| 	def on_trash(self): | 	def on_trash(self): | ||||||
| 		delete_contact_and_address('BankAccount', self.name) | 		delete_contact_and_address('BankAccount', self.name) | ||||||
| 
 | 
 | ||||||
| 	def validate(self): | 	def validate(self): | ||||||
| 		self.validate_company() | 		self.validate_company() | ||||||
|  | 		self.validate_iban() | ||||||
| 
 | 
 | ||||||
| 	def validate_company(self): | 	def validate_company(self): | ||||||
| 		if self.is_company_account and not self.company: | 		if self.is_company_account and not self.company: | ||||||
| 			frappe.throw(_("Company is manadatory for company account")) | 			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 | ||||||
|  | 
 | ||||||
|  | 		def encode_char(c): | ||||||
|  | 			# Position in the alphabet (A=1, B=2, ...) plus nine | ||||||
|  | 			return str(9 + ord(c) - 64) | ||||||
|  | 
 | ||||||
|  | 		# remove whitespaces, upper case to get the right number from ord() | ||||||
|  | 		iban = ''.join(self.iban.split(' ')).upper() | ||||||
|  | 
 | ||||||
|  | 		# Move country code and checksum from the start to the end | ||||||
|  | 		flipped = iban[4:] + iban[:4] | ||||||
|  | 
 | ||||||
|  | 		# Encode characters as numbers | ||||||
|  | 		encoded = [encode_char(c) if ord(c) >= 65 and ord(c) <= 90 else c for c in flipped] | ||||||
|  | 
 | ||||||
|  | 		to_check = int(''.join(encoded)) | ||||||
|  | 
 | ||||||
|  | 		if to_check % 97 != 1: | ||||||
|  | 			frappe.throw(_('IBAN is not valid')) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @frappe.whitelist() | @frappe.whitelist() | ||||||
| def make_bank_account(doctype, docname): | def make_bank_account(doctype, docname): | ||||||
| 	doc = frappe.new_doc("Bank Account") | 	doc = frappe.new_doc("Bank Account") | ||||||
|  | |||||||
| @ -4,9 +4,46 @@ | |||||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||||
| 
 | 
 | ||||||
| import frappe | import frappe | ||||||
|  | from  frappe import _ | ||||||
|  | from  frappe import ValidationError | ||||||
| import unittest | import unittest | ||||||
| 
 | 
 | ||||||
| # test_records = frappe.get_test_records('Bank Account') | # test_records = frappe.get_test_records('Bank Account') | ||||||
| 
 | 
 | ||||||
| class TestBankAccount(unittest.TestCase): | class TestBankAccount(unittest.TestCase): | ||||||
| 	pass | 
 | ||||||
|  | 	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' | ||||||
|  | 		] | ||||||
|  | 
 | ||||||
|  | 		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' | ||||||
|  | 		] | ||||||
|  | 
 | ||||||
|  | 		bank_account = frappe.get_doc({'doctype':'Bank Account'}) | ||||||
|  | 
 | ||||||
|  | 		try: | ||||||
|  | 			bank_account.validate_iban() | ||||||
|  | 		except AttributeError: | ||||||
|  | 			msg = _('BankAccount.validate_iban() failed for empty IBAN') | ||||||
|  | 			self.fail(msg=msg) | ||||||
|  | 
 | ||||||
|  | 		for iban in valid_ibans: | ||||||
|  | 			bank_account.iban = iban | ||||||
|  | 			try: | ||||||
|  | 				bank_account.validate_iban() | ||||||
|  | 			except ValidationError: | ||||||
|  | 				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)) | ||||||
|  | 			with self.assertRaises(ValidationError, msg=msg): | ||||||
|  | 				bank_account.validate_iban() | ||||||
|  | |||||||
| @ -48,7 +48,7 @@ class BankStatementTransactionEntry(Document): | |||||||
| 
 | 
 | ||||||
| 	def get_statement_headers(self): | 	def get_statement_headers(self): | ||||||
| 		if not self.bank_settings: | 		if not self.bank_settings: | ||||||
| 			frappe.throw("Bank Data mapper doesn't exist") | 			frappe.throw(_("Bank Data mapper doesn't exist")) | ||||||
| 		mapper_doc = frappe.get_doc("Bank Statement Settings", self.bank_settings) | 		mapper_doc = frappe.get_doc("Bank Statement Settings", self.bank_settings) | ||||||
| 		headers = {entry.mapped_header:entry.stmt_header for entry in mapper_doc.header_items} | 		headers = {entry.mapped_header:entry.stmt_header for entry in mapper_doc.header_items} | ||||||
| 		return headers | 		return headers | ||||||
| @ -57,7 +57,7 @@ class BankStatementTransactionEntry(Document): | |||||||
| 		if self.bank_statement is None: return | 		if self.bank_statement is None: return | ||||||
| 		filename = self.bank_statement.split("/")[-1] | 		filename = self.bank_statement.split("/")[-1] | ||||||
| 		if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0): | 		if (len(self.new_transaction_items + self.reconciled_transaction_items) > 0): | ||||||
| 			frappe.throw("Transactions already retreived from the statement") | 			frappe.throw(_("Transactions already retreived from the statement")) | ||||||
| 
 | 
 | ||||||
| 		date_format = frappe.get_value("Bank Statement Settings", self.bank_settings, "date_format") | 		date_format = frappe.get_value("Bank Statement Settings", self.bank_settings, "date_format") | ||||||
| 		if (date_format is None): | 		if (date_format is None): | ||||||
| @ -314,7 +314,7 @@ class BankStatementTransactionEntry(Document): | |||||||
| 			try: | 			try: | ||||||
| 				reconcile_against_document(lst) | 				reconcile_against_document(lst) | ||||||
| 			except: | 			except: | ||||||
| 				frappe.throw("Exception occurred while reconciling {0}".format(payment.reference_name)) | 				frappe.throw(_("Exception occurred while reconciling {0}".format(payment.reference_name))) | ||||||
| 
 | 
 | ||||||
| 	def submit_payment_entries(self): | 	def submit_payment_entries(self): | ||||||
| 		for payment in self.new_transaction_items: | 		for payment in self.new_transaction_items: | ||||||
| @ -414,7 +414,7 @@ def get_transaction_entries(filename, headers): | |||||||
| 	elif (filename.lower().endswith("xls")): | 	elif (filename.lower().endswith("xls")): | ||||||
| 		rows = get_rows_from_xls_file(filename) | 		rows = get_rows_from_xls_file(filename) | ||||||
| 	else: | 	else: | ||||||
| 		frappe.throw("Only .csv and .xlsx files are supported currently") | 		frappe.throw(_("Only .csv and .xlsx files are supported currently")) | ||||||
| 
 | 
 | ||||||
| 	stmt_headers = headers.values() | 	stmt_headers = headers.values() | ||||||
| 	for row in rows: | 	for row in rows: | ||||||
|  | |||||||
| @ -0,0 +1,32 @@ | |||||||
|  | // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 | ||||||
|  | // For license information, please see license.txt
 | ||||||
|  | 
 | ||||||
|  | frappe.ui.form.on('Bank Transaction', { | ||||||
|  | 	onload(frm) { | ||||||
|  | 		frm.set_query('payment_document', 'payment_entries', function() { | ||||||
|  | 			return { | ||||||
|  | 				"filters": { | ||||||
|  | 					"name": ["in", ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Expense Claim"]] | ||||||
|  | 				} | ||||||
|  | 			}; | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | frappe.ui.form.on('Bank Transaction Payments', { | ||||||
|  | 	payment_entries_remove: function(frm, cdt, cdn) { | ||||||
|  | 		update_clearance_date(frm, cdt, cdn); | ||||||
|  | 	} | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | const update_clearance_date = (frm, cdt, cdn) => { | ||||||
|  | 	if (frm.doc.docstatus === 1) { | ||||||
|  | 		frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment', | ||||||
|  | 			{doctype: cdt, docname: cdn}) | ||||||
|  | 			.then(e => { | ||||||
|  | 				if (e == "success") { | ||||||
|  | 					frappe.show_alert({message:__("Document {0} successfully uncleared", [e]), indicator:'green'}); | ||||||
|  | 				} | ||||||
|  | 			}); | ||||||
|  | 	} | ||||||
|  | }; | ||||||
							
								
								
									
										833
									
								
								erpnext/accounts/doctype/bank_transaction/bank_transaction.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										833
									
								
								erpnext/accounts/doctype/bank_transaction/bank_transaction.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,833 @@ | |||||||
|  | { | ||||||
|  |  "allow_copy": 0, | ||||||
|  |  "allow_events_in_timeline": 0, | ||||||
|  |  "allow_guest_to_view": 0, | ||||||
|  |  "allow_import": 1, | ||||||
|  |  "allow_rename": 0, | ||||||
|  |  "autoname": "naming_series:", | ||||||
|  |  "beta": 0, | ||||||
|  |  "creation": "2018-10-22 18:19:02.784533", | ||||||
|  |  "custom": 0, | ||||||
|  |  "docstatus": 0, | ||||||
|  |  "doctype": "DocType", | ||||||
|  |  "document_type": "", | ||||||
|  |  "editable_grid": 1, | ||||||
|  |  "engine": "InnoDB", | ||||||
|  |  "fields": [ | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "default": "ACC-BTN-.YYYY.-", | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "naming_series", | ||||||
|  |    "fieldtype": "Select", | ||||||
|  |    "hidden": 1, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "label": "Series", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 1, | ||||||
|  |    "options": "ACC-BTN-.YYYY.-", | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 1, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 1, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 1, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "date", | ||||||
|  |    "fieldtype": "Date", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "label": "Date", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "column_break_2", | ||||||
|  |    "fieldtype": "Column Break", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "default": "Pending", | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "status", | ||||||
|  |    "fieldtype": "Select", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 1, | ||||||
|  |    "label": "Status", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "options": "\nPending\nSettled\nUnreconciled\nReconciled", | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "bank_account", | ||||||
|  |    "fieldtype": "Link", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 1, | ||||||
|  |    "label": "Bank Account", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "options": "Bank Account", | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "default": "", | ||||||
|  |    "fetch_from": "bank_account.company", | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "company", | ||||||
|  |    "fieldtype": "Link", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 1, | ||||||
|  |    "label": "Company", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "options": "Company", | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 1, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "section_break_4", | ||||||
|  |    "fieldtype": "Section Break", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "debit", | ||||||
|  |    "fieldtype": "Currency", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 1, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "label": "Debit", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "credit", | ||||||
|  |    "fieldtype": "Currency", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 1, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "label": "Credit", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "column_break_7", | ||||||
|  |    "fieldtype": "Column Break", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "currency", | ||||||
|  |    "fieldtype": "Link", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "label": "Currency", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "options": "Currency", | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "section_break_10", | ||||||
|  |    "fieldtype": "Section Break", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "description", | ||||||
|  |    "fieldtype": "Small Text", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 1, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "label": "Description", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "section_break_14", | ||||||
|  |    "fieldtype": "Section Break", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "reference_number", | ||||||
|  |    "fieldtype": "Data", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "label": "Reference Number", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "transaction_id", | ||||||
|  |    "fieldtype": "Data", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "label": "Transaction ID", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 1, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 1 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 1, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "payment_entries", | ||||||
|  |    "fieldtype": "Table", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "label": "Payment Entries", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "options": "Bank Transaction Payments", | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "section_break_18", | ||||||
|  |    "fieldtype": "Section Break", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "allocated_amount", | ||||||
|  |    "fieldtype": "Currency", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "label": "Allocated Amount", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "amended_from", | ||||||
|  |    "fieldtype": "Link", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "label": "Amended From", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 1, | ||||||
|  |    "options": "Bank Transaction", | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "print_hide": 1, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 1, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "column_break_17", | ||||||
|  |    "fieldtype": "Column Break", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0, | ||||||
|  |    "allow_in_quick_entry": 0, | ||||||
|  |    "allow_on_submit": 0, | ||||||
|  |    "bold": 0, | ||||||
|  |    "collapsible": 0, | ||||||
|  |    "columns": 0, | ||||||
|  |    "depends_on": "", | ||||||
|  |    "fetch_if_empty": 0, | ||||||
|  |    "fieldname": "unallocated_amount", | ||||||
|  |    "fieldtype": "Currency", | ||||||
|  |    "hidden": 0, | ||||||
|  |    "ignore_user_permissions": 0, | ||||||
|  |    "ignore_xss_filter": 0, | ||||||
|  |    "in_filter": 0, | ||||||
|  |    "in_global_search": 0, | ||||||
|  |    "in_list_view": 0, | ||||||
|  |    "in_standard_filter": 0, | ||||||
|  |    "label": "Unallocated Amount", | ||||||
|  |    "length": 0, | ||||||
|  |    "no_copy": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "precision": "", | ||||||
|  |    "print_hide": 0, | ||||||
|  |    "print_hide_if_no_value": 0, | ||||||
|  |    "read_only": 0, | ||||||
|  |    "remember_last_selected_value": 0, | ||||||
|  |    "report_hide": 0, | ||||||
|  |    "reqd": 0, | ||||||
|  |    "search_index": 0, | ||||||
|  |    "set_only_once": 0, | ||||||
|  |    "translatable": 0, | ||||||
|  |    "unique": 0 | ||||||
|  |   } | ||||||
|  |  ], | ||||||
|  |  "has_web_view": 0, | ||||||
|  |  "hide_heading": 0, | ||||||
|  |  "hide_toolbar": 0, | ||||||
|  |  "idx": 0, | ||||||
|  |  "image_view": 0, | ||||||
|  |  "in_create": 0, | ||||||
|  |  "is_submittable": 1, | ||||||
|  |  "issingle": 0, | ||||||
|  |  "istable": 0, | ||||||
|  |  "max_attachments": 0, | ||||||
|  |  "modified": "2019-05-11 05:27:55.244721", | ||||||
|  |  "modified_by": "Administrator", | ||||||
|  |  "module": "Accounts", | ||||||
|  |  "name": "Bank Transaction", | ||||||
|  |  "name_case": "", | ||||||
|  |  "owner": "Administrator", | ||||||
|  |  "permissions": [ | ||||||
|  |   { | ||||||
|  |    "amend": 0, | ||||||
|  |    "cancel": 1, | ||||||
|  |    "create": 1, | ||||||
|  |    "delete": 1, | ||||||
|  |    "email": 1, | ||||||
|  |    "export": 1, | ||||||
|  |    "if_owner": 0, | ||||||
|  |    "import": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "print": 1, | ||||||
|  |    "read": 1, | ||||||
|  |    "report": 1, | ||||||
|  |    "role": "System Manager", | ||||||
|  |    "set_user_permissions": 0, | ||||||
|  |    "share": 1, | ||||||
|  |    "submit": 1, | ||||||
|  |    "write": 1 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "amend": 0, | ||||||
|  |    "cancel": 1, | ||||||
|  |    "create": 1, | ||||||
|  |    "delete": 1, | ||||||
|  |    "email": 1, | ||||||
|  |    "export": 1, | ||||||
|  |    "if_owner": 0, | ||||||
|  |    "import": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "print": 1, | ||||||
|  |    "read": 1, | ||||||
|  |    "report": 1, | ||||||
|  |    "role": "Accounts Manager", | ||||||
|  |    "set_user_permissions": 0, | ||||||
|  |    "share": 1, | ||||||
|  |    "submit": 1, | ||||||
|  |    "write": 1 | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "amend": 0, | ||||||
|  |    "cancel": 0, | ||||||
|  |    "create": 1, | ||||||
|  |    "delete": 1, | ||||||
|  |    "email": 1, | ||||||
|  |    "export": 1, | ||||||
|  |    "if_owner": 0, | ||||||
|  |    "import": 0, | ||||||
|  |    "permlevel": 0, | ||||||
|  |    "print": 1, | ||||||
|  |    "read": 1, | ||||||
|  |    "report": 1, | ||||||
|  |    "role": "Accounts User", | ||||||
|  |    "set_user_permissions": 0, | ||||||
|  |    "share": 1, | ||||||
|  |    "submit": 1, | ||||||
|  |    "write": 1 | ||||||
|  |   } | ||||||
|  |  ], | ||||||
|  |  "quick_entry": 0, | ||||||
|  |  "read_only": 0, | ||||||
|  |  "read_only_onload": 0, | ||||||
|  |  "show_name_in_global_search": 0, | ||||||
|  |  "sort_field": "date", | ||||||
|  |  "sort_order": "DESC", | ||||||
|  |  "title_field": "bank_account", | ||||||
|  |  "track_changes": 0, | ||||||
|  |  "track_seen": 0, | ||||||
|  |  "track_views": 0 | ||||||
|  | } | ||||||
							
								
								
									
										106
									
								
								erpnext/accounts/doctype/bank_transaction/bank_transaction.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								erpnext/accounts/doctype/bank_transaction/bank_transaction.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,106 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors | ||||||
|  | # For license information, please see license.txt | ||||||
|  | 
 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | import frappe | ||||||
|  | from erpnext.controllers.status_updater import StatusUpdater | ||||||
|  | from frappe.utils import flt | ||||||
|  | from six.moves import reduce | ||||||
|  | from frappe import _ | ||||||
|  | 
 | ||||||
|  | class BankTransaction(StatusUpdater): | ||||||
|  | 	def after_insert(self): | ||||||
|  | 		self.unallocated_amount = abs(flt(self.credit) - flt(self.debit)) | ||||||
|  | 
 | ||||||
|  | 	def on_submit(self): | ||||||
|  | 		self.clear_linked_payment_entries() | ||||||
|  | 		self.set_status() | ||||||
|  | 
 | ||||||
|  | 	def on_update_after_submit(self): | ||||||
|  | 		self.update_allocations() | ||||||
|  | 		self.clear_linked_payment_entries() | ||||||
|  | 		self.set_status(update=True) | ||||||
|  | 
 | ||||||
|  | 	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]) | ||||||
|  | 		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.credit) - flt(self.debit)) - 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.credit) - flt(self.debit))) | ||||||
|  | 
 | ||||||
|  | 		amount = self.debit or self.credit | ||||||
|  | 		if amount == self.allocated_amount: | ||||||
|  | 			frappe.db.set_value(self.doctype, self.name, "status", "Reconciled") | ||||||
|  | 
 | ||||||
|  | 		self.reload() | ||||||
|  | 
 | ||||||
|  | 	def clear_linked_payment_entries(self): | ||||||
|  | 		for payment_entry in self.payment_entries: | ||||||
|  | 			allocated_amount = get_total_allocated_amount(payment_entry) | ||||||
|  | 			paid_amount = get_paid_amount(payment_entry) | ||||||
|  | 
 | ||||||
|  | 			if paid_amount and allocated_amount: | ||||||
|  | 				if  flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount): | ||||||
|  | 					frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).".format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount)))) | ||||||
|  | 				elif flt(allocated_amount[0]["allocated_amount"]) == flt(paid_amount): | ||||||
|  | 					if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]: | ||||||
|  | 						self.clear_simple_entry(payment_entry) | ||||||
|  | 
 | ||||||
|  | 					elif payment_entry.payment_document == "Sales Invoice": | ||||||
|  | 						self.clear_sales_invoice(payment_entry) | ||||||
|  | 
 | ||||||
|  | 	def clear_simple_entry(self, payment_entry): | ||||||
|  | 		frappe.db.set_value(payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", self.date) | ||||||
|  | 
 | ||||||
|  | 	def clear_sales_invoice(self, payment_entry): | ||||||
|  | 		frappe.db.set_value("Sales Invoice Payment", dict(parenttype=payment_entry.payment_document, | ||||||
|  | 			parent=payment_entry.payment_entry), "clearance_date", self.date) | ||||||
|  | 
 | ||||||
|  | def get_total_allocated_amount(payment_entry): | ||||||
|  | 	return frappe.db.sql(""" | ||||||
|  | 		SELECT | ||||||
|  | 			SUM(btp.allocated_amount) as allocated_amount, | ||||||
|  | 			bt.name | ||||||
|  | 		FROM | ||||||
|  | 			`tabBank Transaction Payments` as btp | ||||||
|  | 		LEFT JOIN | ||||||
|  | 			`tabBank Transaction` bt ON bt.name=btp.parent | ||||||
|  | 		WHERE | ||||||
|  | 			btp.payment_document = %s | ||||||
|  | 		AND | ||||||
|  | 			btp.payment_entry = %s | ||||||
|  | 		AND | ||||||
|  | 			bt.docstatus = 1""", (payment_entry.payment_document, payment_entry.payment_entry), as_dict=True) | ||||||
|  | 
 | ||||||
|  | def get_paid_amount(payment_entry): | ||||||
|  | 	if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]: | ||||||
|  | 		return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "paid_amount") | ||||||
|  | 
 | ||||||
|  | 	elif payment_entry.payment_document == "Journal Entry": | ||||||
|  | 		return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_credit") | ||||||
|  | 
 | ||||||
|  | 	elif payment_entry.payment_document == "Expense Claim": | ||||||
|  | 		return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed") | ||||||
|  | 
 | ||||||
|  | 	else: | ||||||
|  | 		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) | ||||||
|  | 		else: | ||||||
|  | 			frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None) | ||||||
|  | 
 | ||||||
|  | 		return doc.payment_entry | ||||||
| @ -0,0 +1,13 @@ | |||||||
|  | // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
 | ||||||
|  | // License: GNU General Public License v3. See license.txt
 | ||||||
|  | 
 | ||||||
|  | frappe.listview_settings['Bank Transaction'] = { | ||||||
|  | 	add_fields: ["unallocated_amount"], | ||||||
|  | 	get_indicator: function(doc) { | ||||||
|  | 		if(flt(doc.unallocated_amount)>0) { | ||||||
|  | 			return [__("Unreconciled"), "orange", "unallocated_amount,>,0"]; | ||||||
|  | 		} else if(flt(doc.unallocated_amount)<=0) { | ||||||
|  | 			return [__("Reconciled"), "green", "unallocated_amount,=,0"]; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | }; | ||||||
| @ -0,0 +1,80 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors | ||||||
|  | # For license information, please see license.txt | ||||||
|  | 
 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | import frappe | ||||||
|  | import json | ||||||
|  | from frappe.utils import getdate | ||||||
|  | from frappe.utils.dateutils import parse_date | ||||||
|  | from six import iteritems | ||||||
|  | 
 | ||||||
|  | @frappe.whitelist() | ||||||
|  | def upload_bank_statement(): | ||||||
|  | 	if getattr(frappe, "uploaded_file", None): | ||||||
|  | 		with open(frappe.uploaded_file, "rb") as upfile: | ||||||
|  | 			fcontent = upfile.read() | ||||||
|  | 	else: | ||||||
|  | 		from frappe.utils.file_manager import get_uploaded_content | ||||||
|  | 		fname, fcontent = get_uploaded_content() | ||||||
|  | 
 | ||||||
|  | 	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')): | ||||||
|  | 		from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file | ||||||
|  | 		rows = read_xlsx_file_from_attached_file(fcontent=fcontent) | ||||||
|  | 
 | ||||||
|  | 	columns = rows[0] | ||||||
|  | 	rows.pop(0) | ||||||
|  | 	data = rows | ||||||
|  | 	return {"columns": columns, "data": data} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @frappe.whitelist() | ||||||
|  | def create_bank_entries(columns, data, bank_account): | ||||||
|  | 	header_map = get_header_mapping(columns, bank_account) | ||||||
|  | 
 | ||||||
|  | 	success = 0 | ||||||
|  | 	errors = 0 | ||||||
|  | 	for d in json.loads(data): | ||||||
|  | 		if all(item is None for item in d) is True: | ||||||
|  | 			continue | ||||||
|  | 		fields = {} | ||||||
|  | 		for key, value in iteritems(header_map): | ||||||
|  | 			fields.update({key: d[int(value)-1]}) | ||||||
|  | 
 | ||||||
|  | 		try: | ||||||
|  | 			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 | ||||||
|  | 			bank_transaction.insert() | ||||||
|  | 			bank_transaction.submit() | ||||||
|  | 			success += 1 | ||||||
|  | 		except Exception: | ||||||
|  | 			frappe.log_error(frappe.get_traceback()) | ||||||
|  | 			errors += 1 | ||||||
|  | 
 | ||||||
|  | 	return {"success": success, "errors": errors} | ||||||
|  | 
 | ||||||
|  | def get_header_mapping(columns, bank_account): | ||||||
|  | 	mapping = get_bank_mapping(bank_account) | ||||||
|  | 
 | ||||||
|  | 	header_map = {} | ||||||
|  | 	for column in json.loads(columns): | ||||||
|  | 		if column["content"] in mapping: | ||||||
|  | 			header_map.update({mapping[column["content"]]: column["colIndex"]}) | ||||||
|  | 
 | ||||||
|  | 	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} | ||||||
|  | 
 | ||||||
|  | 	return mapping | ||||||
| @ -0,0 +1,23 @@ | |||||||
|  | /* eslint-disable */ | ||||||
|  | // rename this file from _test_[name] to test_[name] to activate
 | ||||||
|  | // and remove above this line
 | ||||||
|  | 
 | ||||||
|  | QUnit.test("test: Bank Transaction", function (assert) { | ||||||
|  | 	let done = assert.async(); | ||||||
|  | 
 | ||||||
|  | 	// number of asserts
 | ||||||
|  | 	assert.expect(1); | ||||||
|  | 
 | ||||||
|  | 	frappe.run_serially([ | ||||||
|  | 		// insert a new Bank Transaction
 | ||||||
|  | 		() => frappe.tests.make('Bank Transaction', [ | ||||||
|  | 			// values to be set
 | ||||||
|  | 			{key: 'value'} | ||||||
|  | 		]), | ||||||
|  | 		() => { | ||||||
|  | 			assert.equal(cur_frm.doc.key, 'value'); | ||||||
|  | 		}, | ||||||
|  | 		() => done() | ||||||
|  | 	]); | ||||||
|  | 
 | ||||||
|  | }); | ||||||
| @ -0,0 +1,290 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors | ||||||
|  | # See license.txt | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | 
 | ||||||
|  | import frappe | ||||||
|  | import unittest | ||||||
|  | from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice | ||||||
|  | from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice | ||||||
|  | from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry | ||||||
|  | from erpnext.accounts.page.bank_reconciliation.bank_reconciliation import reconcile, get_linked_payments | ||||||
|  | 
 | ||||||
|  | test_dependencies = ["Item", "Cost Center"] | ||||||
|  | 
 | ||||||
|  | class TestBankTransaction(unittest.TestCase): | ||||||
|  | 	def setUp(self): | ||||||
|  | 		add_transactions() | ||||||
|  | 		add_payments() | ||||||
|  | 
 | ||||||
|  | 	def tearDown(self): | ||||||
|  | 		for bt in frappe.get_all("Bank Transaction"): | ||||||
|  | 			doc = frappe.get_doc("Bank Transaction", bt.name) | ||||||
|  | 			doc.cancel() | ||||||
|  | 			doc.delete() | ||||||
|  | 
 | ||||||
|  | 		# Delete directly in DB to avoid validation errors for countries not allowing deletion | ||||||
|  | 		frappe.db.sql("""delete from `tabPayment Entry Reference`""") | ||||||
|  | 		frappe.db.sql("""delete from `tabPayment Entry`""") | ||||||
|  | 
 | ||||||
|  | 		frappe.flags.test_bank_transactions_created = False | ||||||
|  | 		frappe.flags.test_payments_created = False | ||||||
|  | 
 | ||||||
|  | 	# 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) | ||||||
|  | 		self.assertTrue(linked_payments[0].party == "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/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G")) | ||||||
|  | 		payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200)) | ||||||
|  | 		reconcile(bank_transaction.name, "Payment Entry", payment.name) | ||||||
|  | 
 | ||||||
|  | 		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") | ||||||
|  | 		self.assertTrue(clearance_date is not None) | ||||||
|  | 
 | ||||||
|  | 	# Check if ERPNext can correctly fetch a linked payment based on the party | ||||||
|  | 	def test_linked_payments_based_on_party(self): | ||||||
|  | 		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G")) | ||||||
|  | 		linked_payments = get_linked_payments(bank_transaction.name) | ||||||
|  | 		self.assertTrue(len(linked_payments)==1) | ||||||
|  | 
 | ||||||
|  | 	# 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) | ||||||
|  | 		self.assertTrue(linked_payments[0].payment_type == "Pay") | ||||||
|  | 
 | ||||||
|  | 	# 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")) | ||||||
|  | 		payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1200)) | ||||||
|  | 		reconcile(bank_transaction.name, "Payment Entry", payment.name) | ||||||
|  | 
 | ||||||
|  | 		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)) | ||||||
|  | 		self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name) | ||||||
|  | 
 | ||||||
|  | 	# Raise an error if creditor transaction vs creditor payment | ||||||
|  | 	def test_invalid_creditor_reconcilation(self): | ||||||
|  | 		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="I2015000011 VD/000002514 ATWWXXX AT4701345000003510057 Bio")) | ||||||
|  | 		payment = frappe.get_doc("Payment Entry", dict(party="Conrad Electronic", paid_amount=690)) | ||||||
|  | 		self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name) | ||||||
|  | 
 | ||||||
|  | 	# Raise an error if debitor transaction vs debitor payment | ||||||
|  | 	def test_invalid_debitor_reconcilation(self): | ||||||
|  | 		bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07")) | ||||||
|  | 		payment = frappe.get_doc("Payment Entry", dict(party="Fayva", paid_amount=109080)) | ||||||
|  | 		self.assertRaises(frappe.ValidationError, reconcile, bank_transaction=bank_transaction.name, payment_doctype="Payment Entry", payment_name=payment.name) | ||||||
|  | 
 | ||||||
|  | 	# 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")) | ||||||
|  | 		payment = frappe.get_doc("Sales Invoice", dict(customer="Fayva", status=["=", "Paid"])) | ||||||
|  | 		reconcile(bank_transaction.name, "Sales Invoice", payment.name) | ||||||
|  | 
 | ||||||
|  | 		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 add_transactions(): | ||||||
|  | 	if frappe.flags.test_bank_transactions_created: | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	frappe.set_user("Administrator") | ||||||
|  | 	try: | ||||||
|  | 		frappe.get_doc({ | ||||||
|  | 			"doctype": "Bank", | ||||||
|  | 			"bank_name":"Citi Bank", | ||||||
|  | 		}).insert() | ||||||
|  | 	except frappe.DuplicateEntryError: | ||||||
|  | 		pass | ||||||
|  | 
 | ||||||
|  | 	try: | ||||||
|  | 		frappe.get_doc({ | ||||||
|  | 			"doctype": "Bank Account", | ||||||
|  | 			"account_name":"Checking Account", | ||||||
|  | 			"bank": "Citi Bank", | ||||||
|  | 			"account": "_Test Bank - _TC" | ||||||
|  | 		}).insert() | ||||||
|  | 	except frappe.DuplicateEntryError: | ||||||
|  | 		pass | ||||||
|  | 
 | ||||||
|  | 	doc = frappe.get_doc({ | ||||||
|  | 		"doctype": "Bank Transaction", | ||||||
|  | 		"description":"1512567 BG/000002918 OPSKATTUZWXXX AT776000000098709837 Herr G", | ||||||
|  | 		"date": "2018-10-23", | ||||||
|  | 		"debit": 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", | ||||||
|  | 		"debit": 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", | ||||||
|  | 		"debit": 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", | ||||||
|  | 		"debit": 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", | ||||||
|  | 		"credit": 109080, | ||||||
|  | 		"currency": "INR", | ||||||
|  | 		"bank_account": "Checking Account - Citi Bank" | ||||||
|  | 	}).insert() | ||||||
|  | 	doc.submit() | ||||||
|  | 
 | ||||||
|  | 	frappe.flags.test_bank_transactions_created = True | ||||||
|  | 
 | ||||||
|  | def add_payments(): | ||||||
|  | 	if frappe.flags.test_payments_created: | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	frappe.set_user("Administrator") | ||||||
|  | 
 | ||||||
|  | 	try: | ||||||
|  | 		frappe.get_doc({ | ||||||
|  | 			"doctype": "Supplier", | ||||||
|  | 			"supplier_group":"All Supplier Groups", | ||||||
|  | 			"supplier_type": "Company", | ||||||
|  | 			"supplier_name": "Conrad Electronic" | ||||||
|  | 		}).insert() | ||||||
|  | 
 | ||||||
|  | 	except frappe.DuplicateEntryError: | ||||||
|  | 		pass | ||||||
|  | 
 | ||||||
|  | 	pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690) | ||||||
|  | 	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") | ||||||
|  | 	pe.reference_no = "Conrad Oct 18" | ||||||
|  | 	pe.reference_date = "2018-10-24" | ||||||
|  | 	pe.insert() | ||||||
|  | 	pe.submit() | ||||||
|  | 
 | ||||||
|  | 	try: | ||||||
|  | 		frappe.get_doc({ | ||||||
|  | 			"doctype": "Supplier", | ||||||
|  | 			"supplier_group":"All Supplier Groups", | ||||||
|  | 			"supplier_type": "Company", | ||||||
|  | 			"supplier_name": "Mr G" | ||||||
|  | 		}).insert() | ||||||
|  | 	except frappe.DuplicateEntryError: | ||||||
|  | 		pass | ||||||
|  | 
 | ||||||
|  | 	pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1200) | ||||||
|  | 	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") | ||||||
|  | 	pe.reference_no = "Herr G Oct 18" | ||||||
|  | 	pe.reference_date = "2018-10-24" | ||||||
|  | 	pe.insert() | ||||||
|  | 	pe.submit() | ||||||
|  | 
 | ||||||
|  | 	pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1700) | ||||||
|  | 	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") | ||||||
|  | 	pe.reference_no = "Herr G Nov 18" | ||||||
|  | 	pe.reference_date = "2018-11-01" | ||||||
|  | 	pe.insert() | ||||||
|  | 	pe.submit() | ||||||
|  | 
 | ||||||
|  | 	try: | ||||||
|  | 		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() | ||||||
|  | 	except frappe.DuplicateEntryError: | ||||||
|  | 		pass | ||||||
|  | 
 | ||||||
|  | 	pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900) | ||||||
|  | 	pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") | ||||||
|  | 	pe.reference_no = "Poore Simon's Oct 18" | ||||||
|  | 	pe.reference_date = "2018-10-28" | ||||||
|  | 	pe.insert() | ||||||
|  | 	pe.submit() | ||||||
|  | 
 | ||||||
|  | 	si = create_sales_invoice(customer="Poore Simon's", qty=1, rate=3900) | ||||||
|  | 	pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC") | ||||||
|  | 	pe.reference_no = "Poore Simon's Oct 18" | ||||||
|  | 	pe.reference_date = "2018-10-28" | ||||||
|  | 	pe.insert() | ||||||
|  | 	pe.submit() | ||||||
|  | 
 | ||||||
|  | 	try: | ||||||
|  | 		frappe.get_doc({ | ||||||
|  | 			"doctype": "Customer", | ||||||
|  | 			"customer_group":"All Customer Groups", | ||||||
|  | 			"customer_type": "Company", | ||||||
|  | 			"customer_name": "Fayva" | ||||||
|  | 		}).insert() | ||||||
|  | 	except frappe.DuplicateEntryError: | ||||||
|  | 		pass | ||||||
|  | 
 | ||||||
|  | 	si = create_sales_invoice(customer="Fayva", qty=1, rate=109080) | ||||||
|  | 	pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC") | ||||||
|  | 	pe.reference_no = "Fayva Oct 18" | ||||||
|  | 	pe.reference_date = "2018-10-29" | ||||||
|  | 	pe.insert() | ||||||
|  | 	pe.submit() | ||||||
|  | 
 | ||||||
|  | 	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" | ||||||
|  | 		}) | ||||||
|  | 		mode_of_payment.save() | ||||||
|  | 
 | ||||||
|  | 	si = create_sales_invoice(customer="Fayva", qty=1, rate=109080, do_not_submit=1) | ||||||
|  | 	si.is_pos = 1 | ||||||
|  | 	si.append("payments", { | ||||||
|  | 		"mode_of_payment": "Cash", | ||||||
|  | 		"account": "_Test Bank - _TC", | ||||||
|  | 		"amount": 109080 | ||||||
|  | 	}) | ||||||
|  | 	si.save() | ||||||
|  | 	si.submit() | ||||||
|  | 
 | ||||||
|  | 	frappe.flags.test_payments_created = True | ||||||
| @ -0,0 +1,107 @@ | |||||||
|  | { | ||||||
|  |  "allow_copy": 0,  | ||||||
|  |  "allow_events_in_timeline": 0,  | ||||||
|  |  "allow_guest_to_view": 0,  | ||||||
|  |  "allow_import": 0,  | ||||||
|  |  "allow_rename": 0,  | ||||||
|  |  "beta": 0,  | ||||||
|  |  "creation": "2018-10-24 15:24:56.713277",  | ||||||
|  |  "custom": 0,  | ||||||
|  |  "docstatus": 0,  | ||||||
|  |  "doctype": "DocType",  | ||||||
|  |  "document_type": "",  | ||||||
|  |  "editable_grid": 1,  | ||||||
|  |  "engine": "InnoDB",  | ||||||
|  |  "fields": [ | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0,  | ||||||
|  |    "allow_in_quick_entry": 0,  | ||||||
|  |    "allow_on_submit": 0,  | ||||||
|  |    "bold": 0,  | ||||||
|  |    "collapsible": 0,  | ||||||
|  |    "columns": 0,  | ||||||
|  |    "fieldname": "bank_transaction_field",  | ||||||
|  |    "fieldtype": "Select",  | ||||||
|  |    "hidden": 0,  | ||||||
|  |    "ignore_user_permissions": 0,  | ||||||
|  |    "ignore_xss_filter": 0,  | ||||||
|  |    "in_filter": 0,  | ||||||
|  |    "in_global_search": 0,  | ||||||
|  |    "in_list_view": 1,  | ||||||
|  |    "in_standard_filter": 0,  | ||||||
|  |    "label": "Field in Bank Transaction",  | ||||||
|  |    "length": 0,  | ||||||
|  |    "no_copy": 0,  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "precision": "",  | ||||||
|  |    "print_hide": 0,  | ||||||
|  |    "print_hide_if_no_value": 0,  | ||||||
|  |    "read_only": 0,  | ||||||
|  |    "remember_last_selected_value": 0,  | ||||||
|  |    "report_hide": 0,  | ||||||
|  |    "reqd": 1,  | ||||||
|  |    "search_index": 0,  | ||||||
|  |    "set_only_once": 0,  | ||||||
|  |    "translatable": 0,  | ||||||
|  |    "unique": 0 | ||||||
|  |   },  | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0,  | ||||||
|  |    "allow_in_quick_entry": 0,  | ||||||
|  |    "allow_on_submit": 0,  | ||||||
|  |    "bold": 0,  | ||||||
|  |    "collapsible": 0,  | ||||||
|  |    "columns": 0,  | ||||||
|  |    "fieldname": "file_field",  | ||||||
|  |    "fieldtype": "Data",  | ||||||
|  |    "hidden": 0,  | ||||||
|  |    "ignore_user_permissions": 0,  | ||||||
|  |    "ignore_xss_filter": 0,  | ||||||
|  |    "in_filter": 0,  | ||||||
|  |    "in_global_search": 0,  | ||||||
|  |    "in_list_view": 1,  | ||||||
|  |    "in_standard_filter": 0,  | ||||||
|  |    "label": "Column in Bank File",  | ||||||
|  |    "length": 0,  | ||||||
|  |    "no_copy": 0,  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "precision": "",  | ||||||
|  |    "print_hide": 0,  | ||||||
|  |    "print_hide_if_no_value": 0,  | ||||||
|  |    "read_only": 0,  | ||||||
|  |    "remember_last_selected_value": 0,  | ||||||
|  |    "report_hide": 0,  | ||||||
|  |    "reqd": 1,  | ||||||
|  |    "search_index": 0,  | ||||||
|  |    "set_only_once": 0,  | ||||||
|  |    "translatable": 0,  | ||||||
|  |    "unique": 0 | ||||||
|  |   } | ||||||
|  |  ],  | ||||||
|  |  "has_web_view": 0,  | ||||||
|  |  "hide_heading": 0,  | ||||||
|  |  "hide_toolbar": 0,  | ||||||
|  |  "idx": 0,  | ||||||
|  |  "image_view": 0,  | ||||||
|  |  "in_create": 0,  | ||||||
|  |  "is_submittable": 0,  | ||||||
|  |  "issingle": 0,  | ||||||
|  |  "istable": 1,  | ||||||
|  |  "max_attachments": 0,  | ||||||
|  |  "modified": "2018-10-24 15:24:56.713277",  | ||||||
|  |  "modified_by": "Administrator",  | ||||||
|  |  "module": "Accounts",  | ||||||
|  |  "name": "Bank Transaction Mapping",  | ||||||
|  |  "name_case": "",  | ||||||
|  |  "owner": "Administrator",  | ||||||
|  |  "permissions": [],  | ||||||
|  |  "quick_entry": 0,  | ||||||
|  |  "read_only": 0,  | ||||||
|  |  "read_only_onload": 0,  | ||||||
|  |  "show_name_in_global_search": 0,  | ||||||
|  |  "sort_field": "modified",  | ||||||
|  |  "sort_order": "DESC",  | ||||||
|  |  "track_changes": 0,  | ||||||
|  |  "track_seen": 0,  | ||||||
|  |  "track_views": 0 | ||||||
|  | } | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors | ||||||
|  | # For license information, please see license.txt | ||||||
|  | 
 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | from frappe.model.document import Document | ||||||
|  | 
 | ||||||
|  | class BankTransactionMapping(Document): | ||||||
|  | 	pass | ||||||
| @ -0,0 +1,141 @@ | |||||||
|  | { | ||||||
|  |  "allow_copy": 0,  | ||||||
|  |  "allow_events_in_timeline": 0,  | ||||||
|  |  "allow_guest_to_view": 0,  | ||||||
|  |  "allow_import": 0,  | ||||||
|  |  "allow_rename": 0,  | ||||||
|  |  "beta": 0,  | ||||||
|  |  "creation": "2018-11-28 08:55:40.815355",  | ||||||
|  |  "custom": 0,  | ||||||
|  |  "docstatus": 0,  | ||||||
|  |  "doctype": "DocType",  | ||||||
|  |  "document_type": "",  | ||||||
|  |  "editable_grid": 1,  | ||||||
|  |  "engine": "InnoDB",  | ||||||
|  |  "fields": [ | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0,  | ||||||
|  |    "allow_in_quick_entry": 0,  | ||||||
|  |    "allow_on_submit": 0,  | ||||||
|  |    "bold": 0,  | ||||||
|  |    "collapsible": 0,  | ||||||
|  |    "columns": 0,  | ||||||
|  |    "fieldname": "payment_document",  | ||||||
|  |    "fieldtype": "Link",  | ||||||
|  |    "hidden": 0,  | ||||||
|  |    "ignore_user_permissions": 0,  | ||||||
|  |    "ignore_xss_filter": 0,  | ||||||
|  |    "in_filter": 0,  | ||||||
|  |    "in_global_search": 0,  | ||||||
|  |    "in_list_view": 1,  | ||||||
|  |    "in_standard_filter": 0,  | ||||||
|  |    "label": "Payment Document",  | ||||||
|  |    "length": 0,  | ||||||
|  |    "no_copy": 0,  | ||||||
|  |    "options": "DocType",  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "precision": "",  | ||||||
|  |    "print_hide": 0,  | ||||||
|  |    "print_hide_if_no_value": 0,  | ||||||
|  |    "read_only": 0,  | ||||||
|  |    "remember_last_selected_value": 0,  | ||||||
|  |    "report_hide": 0,  | ||||||
|  |    "reqd": 1,  | ||||||
|  |    "search_index": 0,  | ||||||
|  |    "set_only_once": 0,  | ||||||
|  |    "translatable": 0,  | ||||||
|  |    "unique": 0 | ||||||
|  |   },  | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0,  | ||||||
|  |    "allow_in_quick_entry": 0,  | ||||||
|  |    "allow_on_submit": 0,  | ||||||
|  |    "bold": 0,  | ||||||
|  |    "collapsible": 0,  | ||||||
|  |    "columns": 0,  | ||||||
|  |    "fieldname": "payment_entry",  | ||||||
|  |    "fieldtype": "Dynamic Link",  | ||||||
|  |    "hidden": 0,  | ||||||
|  |    "ignore_user_permissions": 0,  | ||||||
|  |    "ignore_xss_filter": 0,  | ||||||
|  |    "in_filter": 0,  | ||||||
|  |    "in_global_search": 0,  | ||||||
|  |    "in_list_view": 1,  | ||||||
|  |    "in_standard_filter": 0,  | ||||||
|  |    "label": "Payment Entry",  | ||||||
|  |    "length": 0,  | ||||||
|  |    "no_copy": 0,  | ||||||
|  |    "options": "payment_document",  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "precision": "",  | ||||||
|  |    "print_hide": 0,  | ||||||
|  |    "print_hide_if_no_value": 0,  | ||||||
|  |    "read_only": 0,  | ||||||
|  |    "remember_last_selected_value": 0,  | ||||||
|  |    "report_hide": 0,  | ||||||
|  |    "reqd": 1,  | ||||||
|  |    "search_index": 0,  | ||||||
|  |    "set_only_once": 0,  | ||||||
|  |    "translatable": 0,  | ||||||
|  |    "unique": 0 | ||||||
|  |   },  | ||||||
|  |   { | ||||||
|  |    "allow_bulk_edit": 0,  | ||||||
|  |    "allow_in_quick_entry": 0,  | ||||||
|  |    "allow_on_submit": 0,  | ||||||
|  |    "bold": 0,  | ||||||
|  |    "collapsible": 0,  | ||||||
|  |    "columns": 0,  | ||||||
|  |    "fieldname": "allocated_amount",  | ||||||
|  |    "fieldtype": "Currency",  | ||||||
|  |    "hidden": 0,  | ||||||
|  |    "ignore_user_permissions": 0,  | ||||||
|  |    "ignore_xss_filter": 0,  | ||||||
|  |    "in_filter": 0,  | ||||||
|  |    "in_global_search": 0,  | ||||||
|  |    "in_list_view": 1,  | ||||||
|  |    "in_standard_filter": 0,  | ||||||
|  |    "label": "Allocated Amount",  | ||||||
|  |    "length": 0,  | ||||||
|  |    "no_copy": 0,  | ||||||
|  |    "permlevel": 0,  | ||||||
|  |    "precision": "",  | ||||||
|  |    "print_hide": 0,  | ||||||
|  |    "print_hide_if_no_value": 0,  | ||||||
|  |    "read_only": 0,  | ||||||
|  |    "remember_last_selected_value": 0,  | ||||||
|  |    "report_hide": 0,  | ||||||
|  |    "reqd": 1,  | ||||||
|  |    "search_index": 0,  | ||||||
|  |    "set_only_once": 0,  | ||||||
|  |    "translatable": 0,  | ||||||
|  |    "unique": 0 | ||||||
|  |   } | ||||||
|  |  ],  | ||||||
|  |  "has_web_view": 0,  | ||||||
|  |  "hide_heading": 0,  | ||||||
|  |  "hide_toolbar": 0,  | ||||||
|  |  "idx": 0,  | ||||||
|  |  "image_view": 0,  | ||||||
|  |  "in_create": 0,  | ||||||
|  |  "is_submittable": 0,  | ||||||
|  |  "issingle": 0,  | ||||||
|  |  "istable": 1,  | ||||||
|  |  "max_attachments": 0,  | ||||||
|  |  "modified": "2018-12-06 10:57:02.635141",  | ||||||
|  |  "modified_by": "Administrator",  | ||||||
|  |  "module": "Accounts",  | ||||||
|  |  "name": "Bank Transaction Payments",  | ||||||
|  |  "name_case": "",  | ||||||
|  |  "owner": "Administrator",  | ||||||
|  |  "permissions": [],  | ||||||
|  |  "quick_entry": 1,  | ||||||
|  |  "read_only": 0,  | ||||||
|  |  "read_only_onload": 0,  | ||||||
|  |  "show_name_in_global_search": 0,  | ||||||
|  |  "sort_field": "modified",  | ||||||
|  |  "sort_order": "DESC",  | ||||||
|  |  "track_changes": 1,  | ||||||
|  |  "track_seen": 0,  | ||||||
|  |  "track_views": 0 | ||||||
|  | } | ||||||
| @ -0,0 +1,9 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors | ||||||
|  | # For license information, please see license.txt | ||||||
|  | 
 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | from frappe.model.document import Document | ||||||
|  | 
 | ||||||
|  | class BankTransactionPayments(Document): | ||||||
|  | 	pass | ||||||
| @ -24,10 +24,9 @@ class PaymentReconciliation(Document): | |||||||
| 
 | 
 | ||||||
| 	def get_payment_entries(self): | 	def get_payment_entries(self): | ||||||
| 		order_doctype = "Sales Order" if self.party_type=="Customer" else "Purchase Order" | 		order_doctype = "Sales Order" if self.party_type=="Customer" else "Purchase Order" | ||||||
| 
 |  | ||||||
| 		payment_entries = get_advance_payment_entries(self.party_type, self.party, | 		payment_entries = get_advance_payment_entries(self.party_type, self.party, | ||||||
| 			self.receivable_payable_account, order_doctype, against_all_orders=True, limit=self.limit) | 			self.receivable_payable_account, order_doctype, against_all_orders=True, limit=self.limit) | ||||||
| 			 | 
 | ||||||
| 		return payment_entries | 		return payment_entries | ||||||
| 
 | 
 | ||||||
| 	def get_jv_entries(self): | 	def get_jv_entries(self): | ||||||
| @ -37,7 +36,7 @@ class PaymentReconciliation(Document): | |||||||
| 		bank_account_condition = "t2.against_account like %(bank_cash_account)s" \ | 		bank_account_condition = "t2.against_account like %(bank_cash_account)s" \ | ||||||
| 				if self.bank_cash_account else "1=1" | 				if self.bank_cash_account else "1=1" | ||||||
| 
 | 
 | ||||||
| 		limit_cond = "limit %s" % (self.limit or 1000) | 		limit_cond = "limit %s" % self.limit if self.limit else "" | ||||||
| 
 | 
 | ||||||
| 		journal_entries = frappe.db.sql(""" | 		journal_entries = frappe.db.sql(""" | ||||||
| 			select | 			select | ||||||
| @ -84,7 +83,10 @@ class PaymentReconciliation(Document): | |||||||
| 		condition = self.check_condition() | 		condition = self.check_condition() | ||||||
| 
 | 
 | ||||||
| 		non_reconciled_invoices = get_outstanding_invoices(self.party_type, self.party, | 		non_reconciled_invoices = get_outstanding_invoices(self.party_type, self.party, | ||||||
| 			self.receivable_payable_account, condition=condition, limit=self.limit) | 			self.receivable_payable_account, condition=condition) | ||||||
|  | 
 | ||||||
|  | 		if self.limit: | ||||||
|  | 			non_reconciled_invoices = non_reconciled_invoices[:self.limit] | ||||||
| 
 | 
 | ||||||
| 		self.add_invoice_entries(non_reconciled_invoices) | 		self.add_invoice_entries(non_reconciled_invoices) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -220,7 +220,12 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None): | |||||||
| 
 | 
 | ||||||
| 	if args.transaction_type=="selling": | 	if args.transaction_type=="selling": | ||||||
| 		if args.customer and not (args.customer_group and args.territory): | 		if args.customer and not (args.customer_group and args.territory): | ||||||
| 			customer = frappe.get_cached_value("Customer", args.customer, ["customer_group", "territory"]) | 
 | ||||||
|  | 			if args.quotation_to and args.quotation_to != 'Customer': | ||||||
|  | 				customer = frappe._dict() | ||||||
|  | 			else: | ||||||
|  | 				customer = frappe.get_cached_value("Customer", args.customer, ["customer_group", "territory"]) | ||||||
|  | 
 | ||||||
| 			if customer: | 			if customer: | ||||||
| 				args.customer_group, args.territory = customer | 				args.customer_group, args.territory = customer | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -157,7 +157,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ | |||||||
| 	can_change_release_date: function(date) { | 	can_change_release_date: function(date) { | ||||||
| 		const diff = frappe.datetime.get_diff(date, frappe.datetime.nowdate()); | 		const diff = frappe.datetime.get_diff(date, frappe.datetime.nowdate()); | ||||||
| 		if (diff < 0) { | 		if (diff < 0) { | ||||||
| 			frappe.throw('New release date should be in the future'); | 			frappe.throw(__('New release date should be in the future')); | ||||||
| 			return false; | 			return false; | ||||||
| 		} else { | 		} else { | ||||||
| 			return true; | 			return true; | ||||||
| @ -523,8 +523,13 @@ frappe.ui.form.on("Purchase Invoice", { | |||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	onload: function(frm) { | 	onload: function(frm) { | ||||||
| 		if(frm.doc.__onload && !frm.doc.__onload.supplier_tds) { | 		if(frm.doc.__onload) { | ||||||
| 			me.frm.set_df_property("apply_tds", "read_only", 1); | 			if(frm.doc.supplier) { | ||||||
|  | 				frm.doc.apply_tds = frm.doc.__onload.supplier_tds ? 1 : 0; | ||||||
|  | 			} | ||||||
|  | 			if(!frm.doc.__onload.supplier_tds) { | ||||||
|  | 				frm.set_df_property("apply_tds", "read_only", 1); | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		erpnext.queries.setup_queries(frm, "Warehouse", function() { | 		erpnext.queries.setup_queries(frm, "Warehouse", function() { | ||||||
|  | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -105,7 +105,7 @@ class PurchaseInvoice(BuyingController): | |||||||
| 
 | 
 | ||||||
| 	def validate_release_date(self): | 	def validate_release_date(self): | ||||||
| 		if self.release_date and getdate(nowdate()) >= getdate(self.release_date): | 		if self.release_date and getdate(nowdate()) >= getdate(self.release_date): | ||||||
| 			frappe.msgprint('Release date must be in the future', raise_exception=True) | 			frappe.throw(_('Release date must be in the future')) | ||||||
| 
 | 
 | ||||||
| 	def validate_cash(self): | 	def validate_cash(self): | ||||||
| 		if not self.cash_bank_account and flt(self.paid_amount): | 		if not self.cash_bank_account and flt(self.paid_amount): | ||||||
| @ -787,9 +787,8 @@ class PurchaseInvoice(BuyingController): | |||||||
| 		for d in self.items: | 		for d in self.items: | ||||||
| 			if d.project and d.project not in project_list: | 			if d.project and d.project not in project_list: | ||||||
| 				project = frappe.get_doc("Project", d.project) | 				project = frappe.get_doc("Project", d.project) | ||||||
| 				project.flags.dont_sync_tasks = True |  | ||||||
| 				project.update_purchase_costing() | 				project.update_purchase_costing() | ||||||
| 				project.save() | 				project.db_update() | ||||||
| 				project_list.append(d.project) | 				project_list.append(d.project) | ||||||
| 
 | 
 | ||||||
| 	def validate_supplier_invoice(self): | 	def validate_supplier_invoice(self): | ||||||
|  | |||||||
| @ -6,8 +6,8 @@ frappe.listview_settings['Purchase Invoice'] = { | |||||||
| 	add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company", | 	add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company", | ||||||
| 		"currency", "is_return", "release_date", "on_hold"], | 		"currency", "is_return", "release_date", "on_hold"], | ||||||
| 	get_indicator: function(doc) { | 	get_indicator: function(doc) { | ||||||
| 		if(cint(doc.is_return)==1) { | 		if(flt(doc.outstanding_amount) < 0 && doc.docstatus == 1) { | ||||||
| 			return [__("Return"), "darkgrey", "is_return,=,Yes"]; | 			return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<,0"] | ||||||
| 		} else if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) { | 		} else if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) { | ||||||
| 			if(cint(doc.on_hold) && !doc.release_date) { | 			if(cint(doc.on_hold) && !doc.release_date) { | ||||||
| 				return [__("On Hold"), "darkgrey"]; | 				return [__("On Hold"), "darkgrey"]; | ||||||
| @ -18,9 +18,9 @@ frappe.listview_settings['Purchase Invoice'] = { | |||||||
| 			} else { | 			} else { | ||||||
| 				return [__("Unpaid"), "orange", "outstanding_amount,>,0|due,>=,Today"]; | 				return [__("Unpaid"), "orange", "outstanding_amount,>,0|due,>=,Today"]; | ||||||
| 			} | 			} | ||||||
| 		} else if(flt(doc.outstanding_amount) < 0 && doc.docstatus == 1) { | 		} else if(cint(doc.is_return)) { | ||||||
| 			return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<,0"] | 			return [__("Return"), "darkgrey", "is_return,=,Yes"]; | ||||||
| 		}else if(flt(doc.outstanding_amount)==0 && doc.docstatus==1) { | 		} else if(flt(doc.outstanding_amount)==0 && doc.docstatus==1) { | ||||||
| 			return [__("Paid"), "green", "outstanding_amount,=,0"]; | 			return [__("Paid"), "green", "outstanding_amount,=,0"]; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | |||||||
							
								
								
									
										38
									
								
								erpnext/accounts/doctype/sales_invoice/regional/india.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								erpnext/accounts/doctype/sales_invoice/regional/india.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | |||||||
|  | frappe.ui.form.on("Sales Invoice", { | ||||||
|  | 	setup: function(frm) { | ||||||
|  | 		frm.set_query('transporter', function() { | ||||||
|  | 			return { | ||||||
|  | 				filters: { | ||||||
|  | 					'is_transporter': 1 | ||||||
|  | 				} | ||||||
|  | 			}; | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		frm.set_query('driver', function(doc) { | ||||||
|  | 			return { | ||||||
|  | 				filters: { | ||||||
|  | 					'transporter': doc.transporter | ||||||
|  | 				} | ||||||
|  | 			}; | ||||||
|  | 		}); | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	refresh: function(frm) { | ||||||
|  | 		if(frm.doc.docstatus == 1 && !frm.is_dirty() | ||||||
|  | 			&& !frm.doc.is_return && !frm.doc.ewaybill) { | ||||||
|  | 
 | ||||||
|  | 			frm.add_custom_button('e-Way Bill JSON', () => { | ||||||
|  | 				var w = window.open( | ||||||
|  | 					frappe.urllib.get_full_url( | ||||||
|  | 						"/api/method/erpnext.regional.india.utils.generate_ewb_json?" | ||||||
|  | 						+ "dt=" + encodeURIComponent(frm.doc.doctype) | ||||||
|  | 						+ "&dn=" + encodeURIComponent(frm.doc.name) | ||||||
|  | 					) | ||||||
|  | 				); | ||||||
|  | 				if (!w) { | ||||||
|  | 					frappe.msgprint(__("Please enable pop-ups")); return; | ||||||
|  | 				} | ||||||
|  | 			}, __("Make")); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | }); | ||||||
| @ -0,0 +1,33 @@ | |||||||
|  | var globalOnload = frappe.listview_settings['Sales Invoice'].onload; | ||||||
|  | frappe.listview_settings['Sales Invoice'].onload = function (doclist) { | ||||||
|  | 
 | ||||||
|  | 	// Provision in case onload event is added to sales_invoice.js in future
 | ||||||
|  | 	if (globalOnload) { | ||||||
|  | 		globalOnload(doclist); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	const action = () => { | ||||||
|  | 		const selected_docs = doclist.get_checked_items(); | ||||||
|  | 		const docnames = doclist.get_checked_items(true); | ||||||
|  | 
 | ||||||
|  | 		for (let doc of selected_docs) { | ||||||
|  | 			if (doc.docstatus !== 1) { | ||||||
|  | 				frappe.throw(__("e-Way Bill JSON can only be generated from a submitted document")); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		var w = window.open( | ||||||
|  | 			frappe.urllib.get_full_url( | ||||||
|  | 				"/api/method/erpnext.regional.india.utils.generate_ewb_json?" | ||||||
|  | 				+ "dt=" + encodeURIComponent(doclist.doctype) | ||||||
|  | 				+ "&dn=" + encodeURIComponent(docnames) | ||||||
|  | 			) | ||||||
|  | 		); | ||||||
|  | 		if (!w) { | ||||||
|  | 			frappe.msgprint(__("Please enable pop-ups")); return; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	doclist.page.add_actions_menu_item(__('Generate e-Way Bill JSON'), action, false); | ||||||
|  | }; | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -54,8 +54,8 @@ class SalesInvoice(SellingController): | |||||||
| 
 | 
 | ||||||
| 	def set_indicator(self): | 	def set_indicator(self): | ||||||
| 		"""Set indicator for portal""" | 		"""Set indicator for portal""" | ||||||
| 		if cint(self.is_return) == 1: | 		if self.outstanding_amount < 0: | ||||||
| 			self.indicator_title = _("Return") | 			self.indicator_title = _("Credit Note Issued") | ||||||
| 			self.indicator_color = "darkgrey" | 			self.indicator_color = "darkgrey" | ||||||
| 		elif self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()): | 		elif self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()): | ||||||
| 			self.indicator_color = "orange" | 			self.indicator_color = "orange" | ||||||
| @ -63,8 +63,8 @@ class SalesInvoice(SellingController): | |||||||
| 		elif self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()): | 		elif self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()): | ||||||
| 			self.indicator_color = "red" | 			self.indicator_color = "red" | ||||||
| 			self.indicator_title = _("Overdue") | 			self.indicator_title = _("Overdue") | ||||||
| 		elif self.outstanding_amount < 0: | 		elif cint(self.is_return) == 1: | ||||||
| 			self.indicator_title = _("Credit Note Issued") | 			self.indicator_title = _("Return") | ||||||
| 			self.indicator_color = "darkgrey" | 			self.indicator_color = "darkgrey" | ||||||
| 		else: | 		else: | ||||||
| 			self.indicator_color = "green" | 			self.indicator_color = "green" | ||||||
| @ -508,8 +508,8 @@ class SalesInvoice(SellingController): | |||||||
| 			if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes': | 			if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes': | ||||||
| 				for d in self.get('items'): | 				for d in self.get('items'): | ||||||
| 					is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item') | 					is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item') | ||||||
| 					if d.item_code and is_stock_item == 1\ | 					if  (d.item_code and is_stock_item == 1\ | ||||||
| 						and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1]): | 						and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])): | ||||||
| 						msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1) | 						msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @ -1029,9 +1029,8 @@ class SalesInvoice(SellingController): | |||||||
| 	def update_project(self): | 	def update_project(self): | ||||||
| 		if self.project: | 		if self.project: | ||||||
| 			project = frappe.get_doc("Project", self.project) | 			project = frappe.get_doc("Project", self.project) | ||||||
| 			project.flags.dont_sync_tasks = True |  | ||||||
| 			project.update_billed_amount() | 			project.update_billed_amount() | ||||||
| 			project.save() | 			project.db_update() | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| 	def verify_payment_amount_is_positive(self): | 	def verify_payment_amount_is_positive(self): | ||||||
|  | |||||||
| @ -6,16 +6,16 @@ frappe.listview_settings['Sales Invoice'] = { | |||||||
| 	add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company", | 	add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company", | ||||||
| 		"currency", "is_return"], | 		"currency", "is_return"], | ||||||
| 	get_indicator: function(doc) { | 	get_indicator: function(doc) { | ||||||
| 		if(cint(doc.is_return)==1) { | 		if(flt(doc.outstanding_amount) < 0) { | ||||||
| 			return [__("Return"), "darkgrey", "is_return,=,Yes"]; |  | ||||||
| 		} else if(flt(doc.outstanding_amount)==0) { |  | ||||||
| 			return [__("Paid"), "green", "outstanding_amount,=,0"] |  | ||||||
| 		} else if(flt(doc.outstanding_amount) < 0) { |  | ||||||
| 			return [__("Credit Note Issued"), "darkgrey", "outstanding_amount,<,0"] | 			return [__("Credit Note Issued"), "darkgrey", "outstanding_amount,<,0"] | ||||||
| 		}else if (flt(doc.outstanding_amount) > 0 && doc.due_date >= frappe.datetime.get_today()) { | 		} else if (flt(doc.outstanding_amount) > 0 && doc.due_date >= frappe.datetime.get_today()) { | ||||||
| 			return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>,Today"] | 			return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>,Today"] | ||||||
| 		} else if (flt(doc.outstanding_amount) > 0 && doc.due_date < frappe.datetime.get_today()) { | 		} else if (flt(doc.outstanding_amount) > 0 && doc.due_date < frappe.datetime.get_today()) { | ||||||
| 			return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<=,Today"] | 			return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<=,Today"] | ||||||
|  | 		} else if(cint(doc.is_return)) { | ||||||
|  | 			return [__("Return"), "darkgrey", "is_return,=,Yes"]; | ||||||
|  | 		} else if(flt(doc.outstanding_amount)==0) { | ||||||
|  | 			return [__("Paid"), "green", "outstanding_amount,=,0"] | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 	right_column: "grand_total" | 	right_column: "grand_total" | ||||||
|  | |||||||
| @ -19,6 +19,8 @@ from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data | |||||||
| from erpnext.stock.doctype.item.test_item import create_item | from erpnext.stock.doctype.item.test_item import create_item | ||||||
| from six import iteritems | from six import iteritems | ||||||
| from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction | from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction | ||||||
|  | from erpnext.regional.india.utils import get_ewb_data | ||||||
|  | 
 | ||||||
| class TestSalesInvoice(unittest.TestCase): | class TestSalesInvoice(unittest.TestCase): | ||||||
| 	def make(self): | 	def make(self): | ||||||
| 		w = frappe.copy_doc(test_records[0]) | 		w = frappe.copy_doc(test_records[0]) | ||||||
| @ -1421,7 +1423,7 @@ class TestSalesInvoice(unittest.TestCase): | |||||||
| 				"included_in_print_rate": 1 | 				"included_in_print_rate": 1 | ||||||
| 			}) | 			}) | ||||||
| 		si.save() | 		si.save() | ||||||
| 
 | 		si.submit() | ||||||
| 		self.assertEqual(si.net_total, 19453.13) | 		self.assertEqual(si.net_total, 19453.13) | ||||||
| 		self.assertEqual(si.grand_total, 24900) | 		self.assertEqual(si.grand_total, 24900) | ||||||
| 		self.assertEqual(si.total_taxes_and_charges, 5446.88) | 		self.assertEqual(si.total_taxes_and_charges, 5446.88) | ||||||
| @ -1443,6 +1445,50 @@ class TestSalesInvoice(unittest.TestCase): | |||||||
| 			self.assertEqual(expected_values[gle.account][1], gle.debit) | 			self.assertEqual(expected_values[gle.account][1], gle.debit) | ||||||
| 			self.assertEqual(expected_values[gle.account][2], gle.credit) | 			self.assertEqual(expected_values[gle.account][2], gle.credit) | ||||||
| 
 | 
 | ||||||
|  | 	def test_rounding_adjustment_2(self): | ||||||
|  | 		si = create_sales_invoice(rate=400, do_not_save=True) | ||||||
|  | 		for rate in [400, 600, 100]: | ||||||
|  | 			si.append("items", { | ||||||
|  | 				"item_code": "_Test Item", | ||||||
|  | 				"gst_hsn_code": "999800", | ||||||
|  | 				"warehouse": "_Test Warehouse - _TC", | ||||||
|  | 				"qty": 1, | ||||||
|  | 				"rate": rate, | ||||||
|  | 				"income_account": "Sales - _TC", | ||||||
|  | 				"cost_center": "_Test Cost Center - _TC" | ||||||
|  | 			}) | ||||||
|  | 		for tax_account in ["_Test Account VAT - _TC", "_Test Account Service Tax - _TC"]: | ||||||
|  | 			si.append("taxes", { | ||||||
|  | 				"charge_type": "On Net Total", | ||||||
|  | 				"account_head": tax_account, | ||||||
|  | 				"description": tax_account, | ||||||
|  | 				"rate": 9, | ||||||
|  | 				"cost_center": "_Test Cost Center - _TC", | ||||||
|  | 				"included_in_print_rate": 1 | ||||||
|  | 			}) | ||||||
|  | 		si.save() | ||||||
|  | 		si.submit() | ||||||
|  | 		self.assertEqual(si.net_total, 1271.19) | ||||||
|  | 		self.assertEqual(si.grand_total, 1500) | ||||||
|  | 		self.assertEqual(si.total_taxes_and_charges, 228.82) | ||||||
|  | 		self.assertEqual(si.rounding_adjustment, -0.01) | ||||||
|  | 
 | ||||||
|  | 		expected_values = dict((d[0], d) for d in [ | ||||||
|  | 			[si.debit_to, 1500, 0.0], | ||||||
|  | 			["_Test Account Service Tax - _TC", 0.0, 114.41], | ||||||
|  | 			["_Test Account VAT - _TC", 0.0, 114.41], | ||||||
|  | 			["Sales - _TC", 0.0, 1271.18] | ||||||
|  | 		]) | ||||||
|  | 
 | ||||||
|  | 		gl_entries = frappe.db.sql("""select account, debit, credit | ||||||
|  | 			from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s | ||||||
|  | 			order by account asc""", si.name, as_dict=1) | ||||||
|  | 
 | ||||||
|  | 		for gle in gl_entries: | ||||||
|  | 			self.assertEqual(expected_values[gle.account][0], gle.account) | ||||||
|  | 			self.assertEqual(expected_values[gle.account][1], gle.debit) | ||||||
|  | 			self.assertEqual(expected_values[gle.account][2], gle.credit) | ||||||
|  | 
 | ||||||
| 	def test_sales_invoice_with_shipping_rule(self): | 	def test_sales_invoice_with_shipping_rule(self): | ||||||
| 		from erpnext.accounts.doctype.shipping_rule.test_shipping_rule \ | 		from erpnext.accounts.doctype.shipping_rule.test_shipping_rule \ | ||||||
| 			import create_shipping_rule | 			import create_shipping_rule | ||||||
| @ -1681,6 +1727,111 @@ class TestSalesInvoice(unittest.TestCase): | |||||||
| 		self.assertEqual(target_doc.company, "_Test Company 1") | 		self.assertEqual(target_doc.company, "_Test Company 1") | ||||||
| 		self.assertEqual(target_doc.supplier, "_Test Internal Supplier") | 		self.assertEqual(target_doc.supplier, "_Test Internal Supplier") | ||||||
| 
 | 
 | ||||||
|  | 	def test_eway_bill_json(self): | ||||||
|  | 		if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'): | ||||||
|  | 			address = frappe.get_doc({ | ||||||
|  | 				"address_line1": "_Test Address Line 1", | ||||||
|  | 				"address_title": "_Test Address for Eway bill", | ||||||
|  | 				"address_type": "Billing", | ||||||
|  | 				"city": "_Test City", | ||||||
|  | 				"state": "Test State", | ||||||
|  | 				"country": "India", | ||||||
|  | 				"doctype": "Address", | ||||||
|  | 				"is_primary_address": 1, | ||||||
|  | 				"phone": "+91 0000000000", | ||||||
|  | 				"gstin": "27AAECE4835E1ZR", | ||||||
|  | 				"gst_state": "Maharashtra", | ||||||
|  | 				"gst_state_number": "27", | ||||||
|  | 				"pincode": "401108" | ||||||
|  | 			}).insert() | ||||||
|  | 
 | ||||||
|  | 			address.append("links", { | ||||||
|  | 				"link_doctype": "Company", | ||||||
|  | 				"link_name": "_Test Company" | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			address.save() | ||||||
|  | 
 | ||||||
|  | 		if not frappe.db.exists('Address', '_Test Customer-Address for Eway bill-Shipping'): | ||||||
|  | 			address = frappe.get_doc({ | ||||||
|  | 				"address_line1": "_Test Address Line 1", | ||||||
|  | 				"address_title": "_Test Customer-Address for Eway bill", | ||||||
|  | 				"address_type": "Shipping", | ||||||
|  | 				"city": "_Test City", | ||||||
|  | 				"state": "Test State", | ||||||
|  | 				"country": "India", | ||||||
|  | 				"doctype": "Address", | ||||||
|  | 				"is_primary_address": 1, | ||||||
|  | 				"phone": "+91 0000000000", | ||||||
|  | 				"gst_state": "Maharashtra", | ||||||
|  | 				"gst_state_number": "27", | ||||||
|  | 				"pincode": "410038" | ||||||
|  | 			}).insert() | ||||||
|  | 
 | ||||||
|  | 			address.append("links", { | ||||||
|  | 				"link_doctype": "Customer", | ||||||
|  | 				"link_name": "_Test Customer" | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			address.save() | ||||||
|  | 
 | ||||||
|  | 		gst_settings = frappe.get_doc("GST Settings") | ||||||
|  | 
 | ||||||
|  | 		gst_account = frappe.get_all( | ||||||
|  | 			"GST Account", | ||||||
|  | 			fields=["cgst_account", "sgst_account", "igst_account"], | ||||||
|  | 			filters = {"company": "_Test Company"}) | ||||||
|  | 
 | ||||||
|  | 		if not gst_account: | ||||||
|  | 			gst_settings.append("gst_accounts", { | ||||||
|  | 				"company": "_Test Company", | ||||||
|  | 				"cgst_account": "CGST - _TC", | ||||||
|  | 				"sgst_account": "SGST - _TC", | ||||||
|  | 				"igst_account": "IGST - _TC", | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 		gst_settings.save() | ||||||
|  | 
 | ||||||
|  | 		si = create_sales_invoice(do_not_save =1, rate = '60000') | ||||||
|  | 
 | ||||||
|  | 		si.distance = 2000 | ||||||
|  | 		si.company_address = "_Test Address for Eway bill-Billing" | ||||||
|  | 		si.customer_address = "_Test Customer-Address for Eway bill-Shipping" | ||||||
|  | 		si.vehicle_no = "KA12KA1234" | ||||||
|  | 		si.gst_category = "Registered Regular" | ||||||
|  | 
 | ||||||
|  | 		si.append("taxes", { | ||||||
|  | 			"charge_type": "On Net Total", | ||||||
|  | 			"account_head": "CGST - _TC", | ||||||
|  | 			"cost_center": "Main - _TC", | ||||||
|  | 			"description": "CGST @ 9.0", | ||||||
|  | 			"rate": 9 | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		si.append("taxes", { | ||||||
|  | 			"charge_type": "On Net Total", | ||||||
|  | 			"account_head": "SGST - _TC", | ||||||
|  | 			"cost_center": "Main - _TC", | ||||||
|  | 			"description": "SGST @ 9.0", | ||||||
|  | 			"rate": 9 | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		si.submit() | ||||||
|  | 
 | ||||||
|  | 		data = get_ewb_data("Sales Invoice", si.name) | ||||||
|  | 
 | ||||||
|  | 		self.assertEqual(data['version'], '1.0.1118') | ||||||
|  | 		self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR') | ||||||
|  | 		self.assertEqual(data['billLists'][0]['fromTrdName'], '_Test Company') | ||||||
|  | 		self.assertEqual(data['billLists'][0]['toTrdName'], '_Test Customer') | ||||||
|  | 		self.assertEqual(data['billLists'][0]['vehicleType'], 'R') | ||||||
|  | 		self.assertEqual(data['billLists'][0]['totalValue'], 60000) | ||||||
|  | 		self.assertEqual(data['billLists'][0]['cgstValue'], 5400) | ||||||
|  | 		self.assertEqual(data['billLists'][0]['sgstValue'], 5400) | ||||||
|  | 		self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234') | ||||||
|  | 		self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def create_sales_invoice(**args): | def create_sales_invoice(**args): | ||||||
| 	si = frappe.new_doc("Sales Invoice") | 	si = frappe.new_doc("Sales Invoice") | ||||||
| 	args = frappe._dict(args) | 	args = frappe._dict(args) | ||||||
|  | |||||||
| @ -295,7 +295,7 @@ | |||||||
|  "issingle": 0,  |  "issingle": 0,  | ||||||
|  "istable": 1,  |  "istable": 1,  | ||||||
|  "max_attachments": 0,  |  "max_attachments": 0,  | ||||||
|  "modified": "2019-03-06 15:58:37.839241",  |  "modified": "2019-03-19 14:54:56.524556",  | ||||||
|  "modified_by": "Administrator",  |  "modified_by": "Administrator",  | ||||||
|  "module": "Accounts",  |  "module": "Accounts",  | ||||||
|  "name": "Sales Invoice Payment",  |  "name": "Sales Invoice Payment",  | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ | |||||||
| 
 | 
 | ||||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||||
| import frappe | import frappe | ||||||
|  | from frappe import _ | ||||||
| from frappe.model.document import Document | from frappe.model.document import Document | ||||||
| from erpnext.utilities.product import get_price | from erpnext.utilities.product import get_price | ||||||
| 
 | 
 | ||||||
| @ -13,7 +14,7 @@ class SubscriptionPlan(Document): | |||||||
| 
 | 
 | ||||||
| 	def validate_interval_count(self): | 	def validate_interval_count(self): | ||||||
| 		if self.billing_interval_count < 1: | 		if self.billing_interval_count < 1: | ||||||
| 			frappe.throw('Billing Interval Count cannot be less than 1') | 			frappe.throw(_('Billing Interval Count cannot be less than 1')) | ||||||
| 
 | 
 | ||||||
| @frappe.whitelist() | @frappe.whitelist() | ||||||
| def get_plan_rate(plan, quantity=1, customer=None): | def get_plan_rate(plan, quantity=1, customer=None): | ||||||
| @ -26,7 +27,7 @@ def get_plan_rate(plan, quantity=1, customer=None): | |||||||
| 			customer_group = frappe.db.get_value("Customer", customer, "customer_group") | 			customer_group = frappe.db.get_value("Customer", customer, "customer_group") | ||||||
| 		else: | 		else: | ||||||
| 			customer_group = None | 			customer_group = None | ||||||
| 		 | 
 | ||||||
| 		price = get_price(item_code=plan.item, price_list=plan.price_list, customer_group=customer_group, company=None, qty=quantity) | 		price = get_price(item_code=plan.item, price_list=plan.price_list, customer_group=customer_group, company=None, qty=quantity) | ||||||
| 		if not price: | 		if not price: | ||||||
| 			return 0 | 			return 0 | ||||||
|  | |||||||
| @ -145,9 +145,9 @@ def round_off_debit_credit(gl_map): | |||||||
| 			.format(gl_map[0].voucher_type, gl_map[0].voucher_no, debit_credit_diff)) | 			.format(gl_map[0].voucher_type, gl_map[0].voucher_no, debit_credit_diff)) | ||||||
| 
 | 
 | ||||||
| 	elif abs(debit_credit_diff) >= (1.0 / (10**precision)): | 	elif abs(debit_credit_diff) >= (1.0 / (10**precision)): | ||||||
| 		make_round_off_gle(gl_map, debit_credit_diff) | 		make_round_off_gle(gl_map, debit_credit_diff, precision) | ||||||
| 
 | 
 | ||||||
| def make_round_off_gle(gl_map, debit_credit_diff): | def make_round_off_gle(gl_map, debit_credit_diff, precision): | ||||||
| 	round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(gl_map[0].company) | 	round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(gl_map[0].company) | ||||||
| 	round_off_account_exists = False | 	round_off_account_exists = False | ||||||
| 	round_off_gle = frappe._dict() | 	round_off_gle = frappe._dict() | ||||||
| @ -160,6 +160,10 @@ def make_round_off_gle(gl_map, debit_credit_diff): | |||||||
| 				debit_credit_diff += flt(d.credit_in_account_currency) | 				debit_credit_diff += flt(d.credit_in_account_currency) | ||||||
| 			round_off_account_exists = True | 			round_off_account_exists = True | ||||||
| 
 | 
 | ||||||
|  | 	if round_off_account_exists and abs(debit_credit_diff) <= (1.0 / (10**precision)): | ||||||
|  | 		gl_map.remove(round_off_gle) | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
| 	if not round_off_gle: | 	if not round_off_gle: | ||||||
| 		for k in ["voucher_type", "voucher_no", "company", | 		for k in ["voucher_type", "voucher_no", "company", | ||||||
| 			"posting_date", "remarks", "is_opening"]: | 			"posting_date", "remarks", "is_opening"]: | ||||||
|  | |||||||
							
								
								
									
										578
									
								
								erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										578
									
								
								erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,578 @@ | |||||||
|  | frappe.provide("erpnext.accounts"); | ||||||
|  | 
 | ||||||
|  | frappe.pages['bank-reconciliation'].on_page_load = function(wrapper) { | ||||||
|  | 	new erpnext.accounts.bankReconciliation(wrapper); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | erpnext.accounts.bankReconciliation = class BankReconciliation { | ||||||
|  | 	constructor(wrapper) { | ||||||
|  | 		this.page = frappe.ui.make_app_page({ | ||||||
|  | 			parent: wrapper, | ||||||
|  | 			title: __("Bank Reconciliation"), | ||||||
|  | 			single_column: true | ||||||
|  | 		}); | ||||||
|  | 		this.parent = wrapper; | ||||||
|  | 		this.page = this.parent.page; | ||||||
|  | 
 | ||||||
|  | 		this.check_plaid_status(); | ||||||
|  | 		this.make(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	make() { | ||||||
|  | 		const me = this; | ||||||
|  | 
 | ||||||
|  | 		me.$main_section = $(`<div class="reconciliation page-main-content"></div>`).appendTo(me.page.main); | ||||||
|  | 		const empty_state = __("Upload a bank statement, link or reconcile a bank account") | ||||||
|  | 		me.$main_section.append(`<div class="flex justify-center align-center text-muted"
 | ||||||
|  | 			style="height: 50vh; display: flex;"><h5 class="text-muted">${empty_state}</h5></div>`) | ||||||
|  | 
 | ||||||
|  | 		me.page.add_field({ | ||||||
|  | 			fieldtype: 'Link', | ||||||
|  | 			label: __('Company'), | ||||||
|  | 			fieldname: 'company', | ||||||
|  | 			options: "Company", | ||||||
|  | 			onchange: function() { | ||||||
|  | 				if (this.value) { | ||||||
|  | 					me.company = this.value; | ||||||
|  | 				} else { | ||||||
|  | 					me.company = null; | ||||||
|  | 					me.bank_account = null; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 		me.page.add_field({ | ||||||
|  | 			fieldtype: 'Link', | ||||||
|  | 			label: __('Bank Account'), | ||||||
|  | 			fieldname: 'bank_account', | ||||||
|  | 			options: "Bank Account", | ||||||
|  | 			get_query: function() { | ||||||
|  | 				if(!me.company) { | ||||||
|  | 					frappe.throw(__("Please select company first")); | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				return { | ||||||
|  | 					filters: { | ||||||
|  | 						"company": me.company | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			}, | ||||||
|  | 			onchange: function() { | ||||||
|  | 				if (this.value) { | ||||||
|  | 					me.bank_account = this.value; | ||||||
|  | 					me.add_actions(); | ||||||
|  | 				} else { | ||||||
|  | 					me.bank_account = null; | ||||||
|  | 					me.page.hide_actions_menu(); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	check_plaid_status() { | ||||||
|  | 		const me = this; | ||||||
|  | 		frappe.db.get_value("Plaid Settings", "Plaid Settings", "enabled", (r) => { | ||||||
|  | 			if (r && r.enabled == "1") { | ||||||
|  | 				me.plaid_status = "active" | ||||||
|  | 			} else { | ||||||
|  | 				me.plaid_status = "inactive" | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	add_actions() { | ||||||
|  | 		const me = this; | ||||||
|  | 
 | ||||||
|  | 		me.page.show_menu() | ||||||
|  | 
 | ||||||
|  | 		me.page.add_menu_item(__("Upload a statement"), function() { | ||||||
|  | 			me.clear_page_content(); | ||||||
|  | 			new erpnext.accounts.bankTransactionUpload(me); | ||||||
|  | 		}, true) | ||||||
|  | 
 | ||||||
|  | 		if (me.plaid_status==="active") { | ||||||
|  | 			me.page.add_menu_item(__("Synchronize this account"), function() { | ||||||
|  | 				me.clear_page_content(); | ||||||
|  | 				new erpnext.accounts.bankTransactionSync(me); | ||||||
|  | 			}, true) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		me.page.add_menu_item(__("Reconcile this account"), function() { | ||||||
|  | 			me.clear_page_content(); | ||||||
|  | 			me.make_reconciliation_tool(); | ||||||
|  | 		}, true) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	clear_page_content() { | ||||||
|  | 		const me = this; | ||||||
|  | 		$(me.page.body).find('.frappe-list').remove(); | ||||||
|  | 		me.$main_section.empty(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	make_reconciliation_tool() { | ||||||
|  | 		const me = this; | ||||||
|  | 		frappe.model.with_doctype("Bank Transaction", () => { | ||||||
|  | 			erpnext.accounts.ReconciliationList = new erpnext.accounts.ReconciliationTool({ | ||||||
|  | 				parent: me.parent, | ||||||
|  | 				doctype: "Bank Transaction" | ||||||
|  | 			}); | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | erpnext.accounts.bankTransactionUpload = class bankTransactionUpload { | ||||||
|  | 	constructor(parent) { | ||||||
|  | 		this.parent = parent; | ||||||
|  | 		this.data = []; | ||||||
|  | 
 | ||||||
|  | 		const assets = [ | ||||||
|  | 			"/assets/frappe/css/frappe-datatable.css", | ||||||
|  | 			"/assets/frappe/js/lib/clusterize.min.js", | ||||||
|  | 			"/assets/frappe/js/lib/Sortable.min.js", | ||||||
|  | 			"/assets/frappe/js/lib/frappe-datatable.js" | ||||||
|  | 		]; | ||||||
|  | 
 | ||||||
|  | 		frappe.require(assets, () => { | ||||||
|  | 			this.make(); | ||||||
|  | 		}); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	make() { | ||||||
|  | 		const me = this; | ||||||
|  | 		frappe.upload.make({ | ||||||
|  | 			args: { | ||||||
|  | 				method: 'erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.upload_bank_statement', | ||||||
|  | 				allow_multiple: 0 | ||||||
|  | 			}, | ||||||
|  | 			no_socketio: true, | ||||||
|  | 			sample_url: "e.g. http://example.com/somefile.csv", | ||||||
|  | 			callback: function(attachment, r) { | ||||||
|  | 				if (!r.exc && r.message) { | ||||||
|  | 					me.data = r.message; | ||||||
|  | 					me.setup_transactions_dom(); | ||||||
|  | 					me.create_datatable(); | ||||||
|  | 					me.add_primary_action(); | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	setup_transactions_dom() { | ||||||
|  | 		const me = this; | ||||||
|  | 		me.parent.$main_section.append(`<div class="transactions-table"></div>`) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	create_datatable() { | ||||||
|  | 		try { | ||||||
|  | 			this.datatable = new DataTable('.transactions-table', { | ||||||
|  | 				columns: this.data.columns, | ||||||
|  | 				data: this.data.data | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 		catch(err) { | ||||||
|  | 			let msg = __(`Your file could not be processed by ERPNext.
 | ||||||
|  | 						<br>It should be a standard CSV or XLSX file. | ||||||
|  | 						<br>The headers should be in the first row.`)
 | ||||||
|  | 			frappe.throw(msg) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	add_primary_action() { | ||||||
|  | 		const me = this; | ||||||
|  | 		me.parent.page.set_primary_action(__("Submit"), function() { | ||||||
|  | 			me.add_bank_entries() | ||||||
|  | 		}, null, __("Creating bank entries...")) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	add_bank_entries() { | ||||||
|  | 		const me = this; | ||||||
|  | 		frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.create_bank_entries', | ||||||
|  | 			{columns: this.datatable.datamanager.columns, data: this.datatable.datamanager.data, bank_account: me.parent.bank_account} | ||||||
|  | 		).then((result) => { | ||||||
|  | 			let result_title = result.errors == 0 ? __("{0} bank transaction(s) created", [result.success]) : __("{0} bank transaction(s) created and {1} errors", [result.success, result.errors]) | ||||||
|  | 			let result_msg = ` | ||||||
|  | 				<div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;"> | ||||||
|  | 					<h5 class="text-muted">${result_title}</h5> | ||||||
|  | 				</div>` | ||||||
|  | 			me.parent.page.clear_primary_action(); | ||||||
|  | 			me.parent.$main_section.empty(); | ||||||
|  | 			me.parent.$main_section.append(result_msg); | ||||||
|  | 			if (result.errors == 0) { | ||||||
|  | 				frappe.show_alert({message:__("All bank transactions have been created"), indicator:'green'}); | ||||||
|  | 			} else { | ||||||
|  | 				frappe.show_alert({message:__("Please check the error log for details about the import errors"), indicator:'red'}); | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | erpnext.accounts.bankTransactionSync = class bankTransactionSync { | ||||||
|  | 	constructor(parent) { | ||||||
|  | 		this.parent = parent; | ||||||
|  | 		this.data = []; | ||||||
|  | 
 | ||||||
|  | 		this.init_config() | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	init_config() { | ||||||
|  | 		const me = this; | ||||||
|  | 		frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.plaid_configuration') | ||||||
|  | 		.then(result => { | ||||||
|  | 			me.plaid_env = result.plaid_env; | ||||||
|  | 			me.plaid_public_key = result.plaid_public_key; | ||||||
|  | 			me.client_name = result.client_name; | ||||||
|  | 			me.sync_transactions() | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sync_transactions() { | ||||||
|  | 		const me = this; | ||||||
|  | 		frappe.db.get_value("Bank Account", me.parent.bank_account, "bank", (v) => { | ||||||
|  | 			frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.sync_transactions', { | ||||||
|  | 				bank: v['bank'], | ||||||
|  | 				bank_account: me.parent.bank_account, | ||||||
|  | 				freeze: true | ||||||
|  | 			}) | ||||||
|  | 			.then((result) => { | ||||||
|  | 				let result_title = (result.length > 0) ? __("{0} bank transaction(s) created", [result.length]) : __("This bank account is already synchronized") | ||||||
|  | 				let result_msg = ` | ||||||
|  | 					<div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;"> | ||||||
|  | 						<h5 class="text-muted">${result_title}</h5> | ||||||
|  | 					</div>` | ||||||
|  | 				this.parent.$main_section.append(result_msg) | ||||||
|  | 				frappe.show_alert({message:__("Bank account '{0}' has been synchronized", [me.parent.bank_account]), indicator:'green'}); | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | erpnext.accounts.ReconciliationTool = class ReconciliationTool extends frappe.views.BaseList { | ||||||
|  | 	constructor(opts) { | ||||||
|  | 		super(opts); | ||||||
|  | 		this.show(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	setup_defaults() { | ||||||
|  | 		super.setup_defaults(); | ||||||
|  | 
 | ||||||
|  | 		this.page_title = __("Bank Reconciliation"); | ||||||
|  | 		this.doctype = 'Bank Transaction'; | ||||||
|  | 		this.fields = ['date', 'description', 'debit', 'credit', 'currency'] | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	setup_view() { | ||||||
|  | 		this.render_header(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	setup_side_bar() { | ||||||
|  | 		//
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	make_standard_filters() { | ||||||
|  | 		//
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	freeze() { | ||||||
|  | 		this.$result.find('.list-count').html(`<span>${__('Refreshing')}...</span>`); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	get_args() { | ||||||
|  | 		const args = super.get_args(); | ||||||
|  | 
 | ||||||
|  | 		return Object.assign({}, args, { | ||||||
|  | 			...args.filters.push(["Bank Transaction", "docstatus", "=", 1], | ||||||
|  | 				["Bank Transaction", "unallocated_amount", ">", 0]) | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	update_data(r) { | ||||||
|  | 		let data = r.message || []; | ||||||
|  | 
 | ||||||
|  | 		if (this.start === 0) { | ||||||
|  | 			this.data = data; | ||||||
|  | 		} else { | ||||||
|  | 			this.data = this.data.concat(data); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	render() { | ||||||
|  | 		const me = this; | ||||||
|  | 		this.$result.find('.list-row-container').remove(); | ||||||
|  | 		$('[data-fieldname="name"]').remove(); | ||||||
|  | 		me.data.map((value) => { | ||||||
|  | 			const row = $('<div class="list-row-container">').data("data", value).appendTo(me.$result).get(0); | ||||||
|  | 			new erpnext.accounts.ReconciliationRow(row, value); | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	render_header() { | ||||||
|  | 		const me = this; | ||||||
|  | 		if ($(this.wrapper).find('.transaction-header').length === 0) { | ||||||
|  | 			me.$result.append(frappe.render_template("bank_transaction_header")); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | erpnext.accounts.ReconciliationRow = class ReconciliationRow { | ||||||
|  | 	constructor(row, data) { | ||||||
|  | 		this.data = data; | ||||||
|  | 		this.row = row; | ||||||
|  | 		this.make(); | ||||||
|  | 		this.bind_events(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	make() { | ||||||
|  | 		$(this.row).append(frappe.render_template("bank_transaction_row", this.data)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	bind_events() { | ||||||
|  | 		const me = this; | ||||||
|  | 		$(me.row).on('click', '.clickable-section', function() { | ||||||
|  | 			me.bank_entry = $(this).attr("data-name"); | ||||||
|  | 			me.show_dialog($(this).attr("data-name")); | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		$(me.row).on('click', '.new-reconciliation', function() { | ||||||
|  | 			me.bank_entry = $(this).attr("data-name"); | ||||||
|  | 			me.show_dialog($(this).attr("data-name")); | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		$(me.row).on('click', '.new-payment', function() { | ||||||
|  | 			me.bank_entry = $(this).attr("data-name"); | ||||||
|  | 			me.new_payment(); | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		$(me.row).on('click', '.new-invoice', function() { | ||||||
|  | 			me.bank_entry = $(this).attr("data-name"); | ||||||
|  | 			me.new_invoice(); | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		$(me.row).on('click', '.new-expense', function() { | ||||||
|  | 			me.bank_entry = $(this).attr("data-name"); | ||||||
|  | 			me.new_expense(); | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	new_payment() { | ||||||
|  | 		const me = this; | ||||||
|  | 		const paid_amount = me.data.credit > 0 ? me.data.credit : me.data.debit; | ||||||
|  | 		const payment_type = me.data.credit > 0 ? "Receive": "Pay"; | ||||||
|  | 		const party_type = me.data.credit > 0 ? "Customer": "Supplier"; | ||||||
|  | 
 | ||||||
|  | 		frappe.new_doc("Payment Entry", {"payment_type": payment_type, "paid_amount": paid_amount, | ||||||
|  | 			"party_type": party_type, "paid_from": me.data.bank_account}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	new_invoice() { | ||||||
|  | 		const me = this; | ||||||
|  | 		const invoice_type = me.data.credit > 0 ? "Sales Invoice" : "Purchase Invoice"; | ||||||
|  | 
 | ||||||
|  | 		frappe.new_doc(invoice_type) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	new_expense() { | ||||||
|  | 		frappe.new_doc("Expense Claim") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	show_dialog(data) { | ||||||
|  | 		const me = this; | ||||||
|  | 
 | ||||||
|  | 		frappe.db.get_value("Bank Account", me.data.bank_account, "account", (r) => { | ||||||
|  | 			me.gl_account = r.account; | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.get_linked_payments', | ||||||
|  | 			{bank_transaction: data, freeze:true, freeze_message:__("Finding linked payments")} | ||||||
|  | 		).then((result) => { | ||||||
|  | 			me.make_dialog(result) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	make_dialog(data) { | ||||||
|  | 		const me = this; | ||||||
|  | 		me.selected_payment = null; | ||||||
|  | 
 | ||||||
|  | 		const fields = [ | ||||||
|  | 			{ | ||||||
|  | 				fieldtype: 'Section Break', | ||||||
|  | 				fieldname: 'section_break_1', | ||||||
|  | 				label: __('Automatic Reconciliation') | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				fieldtype: 'HTML', | ||||||
|  | 				fieldname: 'payment_proposals' | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				fieldtype: 'Section Break', | ||||||
|  | 				fieldname: 'section_break_2', | ||||||
|  | 				label: __('Search for a payment') | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				fieldtype: 'Link', | ||||||
|  | 				fieldname: 'payment_doctype', | ||||||
|  | 				options: 'DocType', | ||||||
|  | 				label: 'Payment DocType', | ||||||
|  | 				get_query: () => { | ||||||
|  | 					return { | ||||||
|  | 						filters : { | ||||||
|  | 							"name": ["in", ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Expense Claim"]] | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				fieldtype: 'Column Break', | ||||||
|  | 				fieldname: 'column_break_1', | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				fieldtype: 'Dynamic Link', | ||||||
|  | 				fieldname: 'payment_entry', | ||||||
|  | 				options: 'payment_doctype', | ||||||
|  | 				label: 'Payment Document', | ||||||
|  | 				get_query: () => { | ||||||
|  | 					let dt = this.dialog.fields_dict.payment_doctype.value; | ||||||
|  | 					if (dt === "Payment Entry") { | ||||||
|  | 						return { | ||||||
|  | 							query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.payment_entry_query", | ||||||
|  | 							filters : { | ||||||
|  | 								"bank_account": this.data.bank_account, | ||||||
|  | 								"company": this.data.company | ||||||
|  | 							} | ||||||
|  | 						} | ||||||
|  | 					} else if (dt === "Journal Entry") { | ||||||
|  | 						return { | ||||||
|  | 							query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.journal_entry_query", | ||||||
|  | 							filters : { | ||||||
|  | 								"bank_account": this.data.bank_account, | ||||||
|  | 								"company": this.data.company | ||||||
|  | 							} | ||||||
|  | 						} | ||||||
|  | 					} else if (dt === "Sales Invoice") { | ||||||
|  | 						return { | ||||||
|  | 							query: "erpnext.accounts.page.bank_reconciliation.bank_reconciliation.sales_invoices_query" | ||||||
|  | 						} | ||||||
|  | 					} else if (dt === "Purchase Invoice") { | ||||||
|  | 						return { | ||||||
|  | 							filters : [ | ||||||
|  | 								["Purchase Invoice", "ifnull(clearance_date, '')", "=", ""], | ||||||
|  | 								["Purchase Invoice", "docstatus", "=", 1], | ||||||
|  | 								["Purchase Invoice", "company", "=", this.data.company] | ||||||
|  | 							] | ||||||
|  | 						} | ||||||
|  | 					} else if (dt === "Expense Claim") { | ||||||
|  | 						return { | ||||||
|  | 							filters : [ | ||||||
|  | 								["Expense Claim", "ifnull(clearance_date, '')", "=", ""], | ||||||
|  | 								["Expense Claim", "docstatus", "=", 1], | ||||||
|  | 								["Expense Claim", "company", "=", this.data.company] | ||||||
|  | 							] | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				}, | ||||||
|  | 				onchange: function() { | ||||||
|  | 					if (me.selected_payment !== this.value) { | ||||||
|  | 						me.selected_payment = this.value; | ||||||
|  | 						me.display_payment_details(this); | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				fieldtype: 'Section Break', | ||||||
|  | 				fieldname: 'section_break_3' | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				fieldtype: 'HTML', | ||||||
|  | 				fieldname: 'payment_details' | ||||||
|  | 			}, | ||||||
|  | 		]; | ||||||
|  | 
 | ||||||
|  | 		me.dialog = new frappe.ui.Dialog({ | ||||||
|  | 			title: __("Choose a corresponding payment"), | ||||||
|  | 			fields: fields, | ||||||
|  | 			size: "large" | ||||||
|  | 		}); | ||||||
|  | 
 | ||||||
|  | 		const proposals_wrapper = me.dialog.fields_dict.payment_proposals.$wrapper; | ||||||
|  | 		if (data && data.length > 0) { | ||||||
|  | 			proposals_wrapper.append(frappe.render_template("linked_payment_header")); | ||||||
|  | 			data.map(value => { | ||||||
|  | 				proposals_wrapper.append(frappe.render_template("linked_payment_row", value)) | ||||||
|  | 			}) | ||||||
|  | 		} else { | ||||||
|  | 			const empty_data_msg = __("ERPNext could not find any matching payment entry") | ||||||
|  | 			proposals_wrapper.append(`<div class="text-center"><h5 class="text-muted">${empty_data_msg}</h5></div>`) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		$(me.dialog.body).on('click', '.reconciliation-btn', (e) => { | ||||||
|  | 			const payment_entry = $(e.target).attr('data-name'); | ||||||
|  | 			const payment_doctype = $(e.target).attr('data-doctype'); | ||||||
|  | 			frappe.xcall('erpnext.accounts.page.bank_reconciliation.bank_reconciliation.reconcile', | ||||||
|  | 				{bank_transaction: me.bank_entry, payment_doctype: payment_doctype, payment_name: payment_entry}) | ||||||
|  | 			.then((result) => { | ||||||
|  | 				setTimeout(function(){ | ||||||
|  | 					erpnext.accounts.ReconciliationList.refresh(); | ||||||
|  | 				}, 2000); | ||||||
|  | 				me.dialog.hide(); | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		me.dialog.show(); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	display_payment_details(event) { | ||||||
|  | 		const me = this; | ||||||
|  | 		if (event.value) { | ||||||
|  | 			let dt = me.dialog.fields_dict.payment_doctype.value; | ||||||
|  | 			me.dialog.fields_dict['payment_details'].$wrapper.empty(); | ||||||
|  | 			frappe.db.get_doc(dt, event.value) | ||||||
|  | 			.then(doc => { | ||||||
|  | 				let displayed_docs = [] | ||||||
|  | 				if (dt === "Payment Entry") { | ||||||
|  | 					payment.currency = doc.payment_type == "Receive" ? doc.paid_to_account_currency : doc.paid_from_account_currency; | ||||||
|  | 					payment.doctype = dt | ||||||
|  | 					displayed_docs.push(payment); | ||||||
|  | 				} else if (dt === "Journal Entry") { | ||||||
|  | 					doc.accounts.forEach(payment => { | ||||||
|  | 						if (payment.account === me.gl_account) { | ||||||
|  | 							payment.doctype = dt; | ||||||
|  | 							payment.posting_date = doc.posting_date; | ||||||
|  | 							payment.party = doc.pay_to_recd_from; | ||||||
|  | 							payment.reference_no = doc.cheque_no; | ||||||
|  | 							payment.reference_date = doc.cheque_date; | ||||||
|  | 							payment.currency = payment.account_currency; | ||||||
|  | 							payment.paid_amount = payment.credit > 0 ? payment.credit : payment.debit; | ||||||
|  | 							payment.name = doc.name; | ||||||
|  | 							displayed_docs.push(payment); | ||||||
|  | 						} | ||||||
|  | 					}) | ||||||
|  | 				} else if (dt === "Sales Invoice") { | ||||||
|  | 					doc.payments.forEach(payment => { | ||||||
|  | 						if (payment.clearance_date === null || payment.clearance_date === "") { | ||||||
|  | 							payment.doctype = dt; | ||||||
|  | 							payment.posting_date = doc.posting_date; | ||||||
|  | 							payment.party = doc.customer; | ||||||
|  | 							payment.reference_no = doc.remarks; | ||||||
|  | 							payment.currency = doc.currency; | ||||||
|  | 							payment.paid_amount = payment.amount; | ||||||
|  | 							payment.name = doc.name; | ||||||
|  | 							displayed_docs.push(payment); | ||||||
|  | 						} | ||||||
|  | 					}) | ||||||
|  | 				} | ||||||
|  | 
 | ||||||
|  | 				const details_wrapper = me.dialog.fields_dict.payment_details.$wrapper; | ||||||
|  | 				details_wrapper.append(frappe.render_template("linked_payment_header")); | ||||||
|  | 				displayed_docs.forEach(values => { | ||||||
|  | 					details_wrapper.append(frappe.render_template("linked_payment_row", values)); | ||||||
|  | 				}) | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -0,0 +1,29 @@ | |||||||
|  | { | ||||||
|  |  "content": null,  | ||||||
|  |  "creation": "2018-11-24 12:03:14.646669",  | ||||||
|  |  "docstatus": 0,  | ||||||
|  |  "doctype": "Page",  | ||||||
|  |  "idx": 0,  | ||||||
|  |  "modified": "2018-11-24 12:03:14.646669",  | ||||||
|  |  "modified_by": "Administrator",  | ||||||
|  |  "module": "Accounts",  | ||||||
|  |  "name": "bank-reconciliation",  | ||||||
|  |  "owner": "Administrator",  | ||||||
|  |  "page_name": "bank-reconciliation",  | ||||||
|  |  "roles": [ | ||||||
|  |   { | ||||||
|  |    "role": "System Manager" | ||||||
|  |   },  | ||||||
|  |   { | ||||||
|  |    "role": "Accounts Manager" | ||||||
|  |   },  | ||||||
|  |   { | ||||||
|  |    "role": "Accounts User" | ||||||
|  |   } | ||||||
|  |  ],  | ||||||
|  |  "script": null,  | ||||||
|  |  "standard": "Yes",  | ||||||
|  |  "style": null,  | ||||||
|  |  "system_page": 0,  | ||||||
|  |  "title": "Bank Reconciliation" | ||||||
|  | } | ||||||
							
								
								
									
										378
									
								
								erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										378
									
								
								erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,378 @@ | |||||||
|  | # -*- coding: utf-8 -*- | ||||||
|  | # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors | ||||||
|  | # For license information, please see license.txt | ||||||
|  | 
 | ||||||
|  | from __future__ import unicode_literals | ||||||
|  | import frappe | ||||||
|  | from frappe import _ | ||||||
|  | import difflib | ||||||
|  | from frappe.utils import flt | ||||||
|  | from six import iteritems | ||||||
|  | from erpnext import get_company_currency | ||||||
|  | 
 | ||||||
|  | @frappe.whitelist() | ||||||
|  | def reconcile(bank_transaction, payment_doctype, payment_name): | ||||||
|  | 	transaction = frappe.get_doc("Bank Transaction", bank_transaction) | ||||||
|  | 	payment_entry = frappe.get_doc(payment_doctype, payment_name) | ||||||
|  | 
 | ||||||
|  | 	account = frappe.db.get_value("Bank Account", transaction.bank_account, "account") | ||||||
|  | 	gl_entry = frappe.get_doc("GL Entry", dict(account=account, voucher_type=payment_doctype, voucher_no=payment_name)) | ||||||
|  | 
 | ||||||
|  | 	if transaction.unallocated_amount == 0: | ||||||
|  | 		frappe.throw(_("This bank transaction is already fully reconciled")) | ||||||
|  | 
 | ||||||
|  | 	if transaction.credit > 0 and gl_entry.credit > 0: | ||||||
|  | 		frappe.throw(_("The selected payment entry should be linked with a debtor bank transaction")) | ||||||
|  | 
 | ||||||
|  | 	if transaction.debit > 0 and gl_entry.debit > 0: | ||||||
|  | 		frappe.throw(_("The selected payment entry should be linked with a creditor bank transaction")) | ||||||
|  | 
 | ||||||
|  | 	add_payment_to_transaction(transaction, payment_entry, gl_entry) | ||||||
|  | 
 | ||||||
|  | 	return 'reconciled' | ||||||
|  | 
 | ||||||
|  | def add_payment_to_transaction(transaction, payment_entry, gl_entry): | ||||||
|  | 	gl_amount, transaction_amount = (gl_entry.credit, transaction.debit) if gl_entry.credit > 0 else (gl_entry.debit, transaction.credit) | ||||||
|  | 	allocated_amount = gl_amount if gl_amount <= transaction_amount else transaction_amount | ||||||
|  | 	transaction.append("payment_entries", { | ||||||
|  | 		"payment_document": payment_entry.doctype, | ||||||
|  | 		"payment_entry": payment_entry.name, | ||||||
|  | 		"allocated_amount": allocated_amount | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	transaction.save() | ||||||
|  | 	transaction.update_allocations() | ||||||
|  | 
 | ||||||
|  | @frappe.whitelist() | ||||||
|  | def get_linked_payments(bank_transaction): | ||||||
|  | 	transaction = frappe.get_doc("Bank Transaction", bank_transaction) | ||||||
|  | 	bank_account = frappe.db.get_values("Bank Account", transaction.bank_account, ["account", "company"], as_dict=True) | ||||||
|  | 
 | ||||||
|  | 	# Get all payment entries with a matching amount | ||||||
|  | 	amount_matching = check_matching_amount(bank_account[0].account, bank_account[0].company, transaction) | ||||||
|  | 
 | ||||||
|  | 	# Get some data from payment entries linked to a corresponding bank transaction | ||||||
|  | 	description_matching = get_matching_descriptions_data(bank_account[0].company, transaction) | ||||||
|  | 
 | ||||||
|  | 	if amount_matching: | ||||||
|  | 		return check_amount_vs_description(amount_matching, description_matching) | ||||||
|  | 
 | ||||||
|  | 	elif description_matching: | ||||||
|  | 		description_matching = filter(lambda x: not x.get('clearance_date'), description_matching) | ||||||
|  | 		if not description_matching: | ||||||
|  | 			return [] | ||||||
|  | 
 | ||||||
|  | 		return sorted(list(description_matching), key = lambda x: x["posting_date"], reverse=True) | ||||||
|  | 
 | ||||||
|  | 	else: | ||||||
|  | 		return [] | ||||||
|  | 
 | ||||||
|  | def check_matching_amount(bank_account, company, transaction): | ||||||
|  | 	payments = [] | ||||||
|  | 	amount = transaction.credit if transaction.credit > 0 else transaction.debit | ||||||
|  | 
 | ||||||
|  | 	payment_type = "Receive" if transaction.credit > 0 else "Pay" | ||||||
|  | 	account_from_to = "paid_to" if transaction.credit > 0 else "paid_from" | ||||||
|  | 	currency_field = "paid_to_account_currency as currency" if transaction.credit > 0 else "paid_from_account_currency as currency" | ||||||
|  | 
 | ||||||
|  | 	payment_entries = frappe.get_all("Payment Entry", fields=["'Payment Entry' as doctype", "name", "paid_amount", "payment_type", "reference_no", "reference_date", | ||||||
|  | 		"party", "party_type", "posting_date", "{0}".format(currency_field)], filters=[["paid_amount", "like", "{0}%".format(amount)], | ||||||
|  | 		["docstatus", "=", "1"], ["payment_type", "=", [payment_type, "Internal Transfer"]], ["ifnull(clearance_date, '')", "=", ""], ["{0}".format(account_from_to), "=", "{0}".format(bank_account)]]) | ||||||
|  | 
 | ||||||
|  | 	if transaction.credit > 0: | ||||||
|  | 		journal_entries = frappe.db.sql(""" | ||||||
|  | 			SELECT | ||||||
|  | 				'Journal Entry' as doctype, je.name, je.posting_date, je.cheque_no as reference_no, | ||||||
|  | 				je.pay_to_recd_from as party, je.cheque_date as reference_date, jea.debit_in_account_currency as paid_amount | ||||||
|  | 			FROM | ||||||
|  | 				`tabJournal Entry Account` as jea | ||||||
|  | 			JOIN | ||||||
|  | 				`tabJournal Entry` as je | ||||||
|  | 			ON | ||||||
|  | 				jea.parent = je.name | ||||||
|  | 			WHERE | ||||||
|  | 				(je.clearance_date is null or je.clearance_date='0000-00-00') | ||||||
|  | 			AND | ||||||
|  | 				jea.account = %s | ||||||
|  | 			AND | ||||||
|  | 				jea.debit_in_account_currency like %s | ||||||
|  | 			AND | ||||||
|  | 				je.docstatus = 1 | ||||||
|  | 		""", (bank_account, amount), as_dict=True) | ||||||
|  | 	else: | ||||||
|  | 		journal_entries = frappe.db.sql(""" | ||||||
|  | 			SELECT | ||||||
|  | 				'Journal Entry' as doctype, je.name, je.posting_date, je.cheque_no as reference_no, | ||||||
|  | 				jea.account_currency as currency, je.pay_to_recd_from as party, je.cheque_date as reference_date, | ||||||
|  | 				jea.credit_in_account_currency as paid_amount | ||||||
|  | 			FROM | ||||||
|  | 				`tabJournal Entry Account` as jea | ||||||
|  | 			JOIN | ||||||
|  | 				`tabJournal Entry` as je | ||||||
|  | 			ON | ||||||
|  | 				jea.parent = je.name | ||||||
|  | 			WHERE | ||||||
|  | 				(je.clearance_date is null or je.clearance_date='0000-00-00') | ||||||
|  | 			AND | ||||||
|  | 				jea.account = %(bank_account)s | ||||||
|  | 			AND | ||||||
|  | 				jea.credit_in_account_currency like %(txt)s | ||||||
|  | 			AND | ||||||
|  | 				je.docstatus = 1 | ||||||
|  | 		""", { | ||||||
|  | 			'bank_account': bank_account, | ||||||
|  | 			'txt': '%%%s%%' % amount | ||||||
|  | 		}, as_dict=True) | ||||||
|  | 
 | ||||||
|  | 		frappe.errprint(journal_entries) | ||||||
|  | 
 | ||||||
|  | 	if transaction.credit > 0: | ||||||
|  | 		sales_invoices = frappe.db.sql(""" | ||||||
|  | 			SELECT | ||||||
|  | 				'Sales Invoice' as doctype, si.name, si.customer as party, | ||||||
|  | 				si.posting_date, sip.amount as paid_amount | ||||||
|  | 			FROM | ||||||
|  | 				`tabSales Invoice Payment` as sip | ||||||
|  | 			JOIN | ||||||
|  | 				`tabSales Invoice` as si | ||||||
|  | 			ON | ||||||
|  | 				sip.parent = si.name | ||||||
|  | 			WHERE | ||||||
|  | 				(sip.clearance_date is null or sip.clearance_date='0000-00-00') | ||||||
|  | 			AND | ||||||
|  | 				sip.account = %s | ||||||
|  | 			AND | ||||||
|  | 				sip.amount like %s | ||||||
|  | 			AND | ||||||
|  | 				si.docstatus = 1 | ||||||
|  | 		""", (bank_account, amount), as_dict=True) | ||||||
|  | 	else: | ||||||
|  | 		sales_invoices = [] | ||||||
|  | 
 | ||||||
|  | 	if transaction.debit > 0: | ||||||
|  | 		purchase_invoices = frappe.get_all("Purchase Invoice", | ||||||
|  | 			fields = ["'Purchase Invoice' as doctype", "name", "paid_amount", "supplier as party", "posting_date", "currency"], | ||||||
|  | 			filters=[ | ||||||
|  | 				["paid_amount", "like", "{0}%".format(amount)], | ||||||
|  | 				["docstatus", "=", "1"], | ||||||
|  | 				["is_paid", "=", "1"], | ||||||
|  | 				["ifnull(clearance_date, '')", "=", ""], | ||||||
|  | 				["cash_bank_account", "=", "{0}".format(bank_account)] | ||||||
|  | 			] | ||||||
|  | 		) | ||||||
|  | 
 | ||||||
|  | 		mode_of_payments = [x["parent"] for x in frappe.db.get_list("Mode of Payment Account", | ||||||
|  | 			filters={"default_account": bank_account}, fields=["parent"])] | ||||||
|  | 
 | ||||||
|  | 		company_currency = get_company_currency(company) | ||||||
|  | 
 | ||||||
|  | 		expense_claims = frappe.get_all("Expense Claim", | ||||||
|  | 			fields=["'Expense Claim' as doctype", "name", "total_sanctioned_amount as paid_amount", | ||||||
|  | 				"employee as party", "posting_date", "'{0}' as currency".format(company_currency)], | ||||||
|  | 			filters=[ | ||||||
|  | 				["total_sanctioned_amount", "like", "{0}%".format(amount)], | ||||||
|  | 				["docstatus", "=", "1"], | ||||||
|  | 				["is_paid", "=", "1"], | ||||||
|  | 				["ifnull(clearance_date, '')", "=", ""], | ||||||
|  | 				["mode_of_payment", "in", "{0}".format(tuple(mode_of_payments))] | ||||||
|  | 			] | ||||||
|  | 		) | ||||||
|  | 	else: | ||||||
|  | 		purchase_invoices = expense_claims = [] | ||||||
|  | 
 | ||||||
|  | 	for data in [payment_entries, journal_entries, sales_invoices, purchase_invoices, expense_claims]: | ||||||
|  | 		if data: | ||||||
|  | 			payments.extend(data) | ||||||
|  | 
 | ||||||
|  | 	return payments | ||||||
|  | 
 | ||||||
|  | def get_matching_descriptions_data(company, transaction): | ||||||
|  | 	if not transaction.description : | ||||||
|  | 		return [] | ||||||
|  | 
 | ||||||
|  | 	bank_transactions = frappe.db.sql(""" | ||||||
|  | 		SELECT | ||||||
|  | 			bt.name, bt.description, bt.date, btp.payment_document, btp.payment_entry | ||||||
|  | 		FROM | ||||||
|  | 			`tabBank Transaction` as bt | ||||||
|  | 		LEFT JOIN | ||||||
|  | 			`tabBank Transaction Payments` as btp | ||||||
|  | 		ON | ||||||
|  | 			bt.name = btp.parent | ||||||
|  | 		WHERE | ||||||
|  | 			bt.allocated_amount > 0 | ||||||
|  | 		AND | ||||||
|  | 			bt.docstatus = 1 | ||||||
|  | 		""", as_dict=True) | ||||||
|  | 
 | ||||||
|  | 	selection = [] | ||||||
|  | 	for bank_transaction in bank_transactions: | ||||||
|  | 		if bank_transaction.description: | ||||||
|  | 			seq=difflib.SequenceMatcher(lambda x: x == " ", transaction.description, bank_transaction.description) | ||||||
|  | 
 | ||||||
|  | 			if seq.ratio() > 0.6: | ||||||
|  | 				bank_transaction["ratio"] = seq.ratio() | ||||||
|  | 				selection.append(bank_transaction) | ||||||
|  | 
 | ||||||
|  | 	document_types = set([x["payment_document"] for x in selection]) | ||||||
|  | 
 | ||||||
|  | 	links = {} | ||||||
|  | 	for document_type in document_types: | ||||||
|  | 		links[document_type] = [x["payment_entry"] for x in selection if x["payment_document"]==document_type] | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 	data = [] | ||||||
|  | 	company_currency = get_company_currency(company) | ||||||
|  | 	for key, value in iteritems(links): | ||||||
|  | 		if key == "Payment Entry": | ||||||
|  | 			data.extend(frappe.get_all("Payment Entry", filters=[["name", "in", value]], | ||||||
|  | 				fields=["'Payment Entry' as doctype", "posting_date", "party", "reference_no", | ||||||
|  | 					"reference_date", "paid_amount", "paid_to_account_currency as currency", "clearance_date"])) | ||||||
|  | 		if key == "Journal Entry": | ||||||
|  | 			journal_entries = frappe.get_all("Journal Entry", filters=[["name", "in", value]], | ||||||
|  | 				fields=["name", "'Journal Entry' as doctype", "posting_date", | ||||||
|  | 					"pay_to_recd_from as party", "cheque_no as reference_no", "cheque_date as reference_date", | ||||||
|  | 					"total_credit as paid_amount", "clearance_date"]) | ||||||
|  | 			for journal_entry in journal_entries: | ||||||
|  | 				journal_entry_accounts = frappe.get_all("Journal Entry Account", filters={"parenttype": journal_entry["doctype"], "parent": journal_entry["name"]}, fields=["account_currency"]) | ||||||
|  | 				journal_entry["currency"] = journal_entry_accounts[0]["account_currency"] if journal_entry_accounts else company_currency | ||||||
|  | 			data.extend(journal_entries) | ||||||
|  | 		if key == "Sales Invoice": | ||||||
|  | 			data.extend(frappe.get_all("Sales Invoice", filters=[["name", "in", value]], fields=["'Sales Invoice' as doctype", "posting_date", "customer_name as party", "paid_amount", "currency"])) | ||||||
|  | 		if key == "Purchase Invoice": | ||||||
|  | 			data.extend(frappe.get_all("Purchase Invoice", filters=[["name", "in", value]], fields=["'Purchase Invoice' as doctype", "posting_date", "supplier_name as party", "paid_amount", "currency"])) | ||||||
|  | 		if key == "Expense Claim": | ||||||
|  | 			expense_claims = frappe.get_all("Expense Claim", filters=[["name", "in", value]], fields=["'Expense Claim' as doctype", "posting_date", "employee_name as party", "total_amount_reimbursed as paid_amount"]) | ||||||
|  | 			data.extend([dict(x,**{"currency": company_currency}) for x in expense_claims]) | ||||||
|  | 
 | ||||||
|  | 	return data | ||||||
|  | 
 | ||||||
|  | def check_amount_vs_description(amount_matching, description_matching): | ||||||
|  | 	result = [] | ||||||
|  | 
 | ||||||
|  | 	if description_matching: | ||||||
|  | 		for am_match in amount_matching: | ||||||
|  | 			for des_match in description_matching: | ||||||
|  | 				if des_match.get("clearance_date"): | ||||||
|  | 					continue | ||||||
|  | 
 | ||||||
|  | 				if am_match["party"] == des_match["party"]: | ||||||
|  | 					if am_match not in result: | ||||||
|  | 						result.append(am_match) | ||||||
|  | 						continue | ||||||
|  | 
 | ||||||
|  | 				if "reference_no" in am_match and "reference_no" in des_match: | ||||||
|  | 					if difflib.SequenceMatcher(lambda x: x == " ", am_match["reference_no"], des_match["reference_no"]).ratio() > 70: | ||||||
|  | 						if am_match not in result: | ||||||
|  | 							result.append(am_match) | ||||||
|  | 		if result: | ||||||
|  | 			return sorted(result, key = lambda x: x["posting_date"], reverse=True) | ||||||
|  | 		else: | ||||||
|  | 			return sorted(amount_matching, key = lambda x: x["posting_date"], reverse=True) | ||||||
|  | 
 | ||||||
|  | 	else: | ||||||
|  | 		return sorted(amount_matching, key = lambda x: x["posting_date"], reverse=True) | ||||||
|  | 
 | ||||||
|  | def get_matching_transactions_payments(description_matching): | ||||||
|  | 	payments = [x["payment_entry"] for x in description_matching] | ||||||
|  | 
 | ||||||
|  | 	payment_by_ratio = {x["payment_entry"]: x["ratio"] for x in description_matching} | ||||||
|  | 
 | ||||||
|  | 	if payments: | ||||||
|  | 		reference_payment_list = frappe.get_all("Payment Entry", fields=["name", "paid_amount", "payment_type", "reference_no", "reference_date", | ||||||
|  | 			"party", "party_type", "posting_date", "paid_to_account_currency"], filters=[["name", "in", payments]]) | ||||||
|  | 
 | ||||||
|  | 		return sorted(reference_payment_list, key=lambda x: payment_by_ratio[x["name"]]) | ||||||
|  | 
 | ||||||
|  | 	else: | ||||||
|  | 		return [] | ||||||
|  | 
 | ||||||
|  | def payment_entry_query(doctype, txt, searchfield, start, page_len, filters): | ||||||
|  | 	account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account") | ||||||
|  | 	if not account: | ||||||
|  | 		return | ||||||
|  | 
 | ||||||
|  | 	return frappe.db.sql(""" | ||||||
|  | 		SELECT | ||||||
|  | 			name, party, paid_amount, received_amount, reference_no | ||||||
|  | 		FROM | ||||||
|  | 			`tabPayment Entry` | ||||||
|  | 		WHERE | ||||||
|  | 			(clearance_date is null or clearance_date='0000-00-00') | ||||||
|  | 			AND (paid_from = %(account)s or paid_to = %(account)s) | ||||||
|  | 			AND (name like %(txt)s or party like %(txt)s) | ||||||
|  | 			AND docstatus = 1 | ||||||
|  | 		ORDER BY | ||||||
|  | 			if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), name | ||||||
|  | 		LIMIT | ||||||
|  | 			%(start)s, %(page_len)s""", | ||||||
|  | 		{ | ||||||
|  | 			'txt': "%%%s%%" % txt, | ||||||
|  | 			'_txt': txt.replace("%", ""), | ||||||
|  | 			'start': start, | ||||||
|  | 			'page_len': page_len, | ||||||
|  | 			'account': account | ||||||
|  | 		} | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | def journal_entry_query(doctype, txt, searchfield, start, page_len, filters): | ||||||
|  | 	account = frappe.db.get_value("Bank Account", filters.get("bank_account"), "account") | ||||||
|  | 
 | ||||||
|  | 	return frappe.db.sql(""" | ||||||
|  | 		SELECT | ||||||
|  | 			jea.parent, je.pay_to_recd_from, | ||||||
|  | 			if(jea.debit_in_account_currency > 0, jea.debit_in_account_currency, jea.credit_in_account_currency) | ||||||
|  | 		FROM | ||||||
|  | 			`tabJournal Entry Account` as jea | ||||||
|  | 		LEFT JOIN | ||||||
|  | 			`tabJournal Entry` as je | ||||||
|  | 		ON | ||||||
|  | 			jea.parent = je.name | ||||||
|  | 		WHERE | ||||||
|  | 			(je.clearance_date is null or je.clearance_date='0000-00-00') | ||||||
|  | 		AND | ||||||
|  | 			jea.account = %(account)s | ||||||
|  | 		AND | ||||||
|  | 			(jea.parent like %(txt)s or je.pay_to_recd_from like %(txt)s) | ||||||
|  | 		AND | ||||||
|  | 			je.docstatus = 1 | ||||||
|  | 		ORDER BY | ||||||
|  | 			if(locate(%(_txt)s, jea.parent), locate(%(_txt)s, jea.parent), 99999), | ||||||
|  | 			jea.parent | ||||||
|  | 		LIMIT | ||||||
|  | 			%(start)s, %(page_len)s""", | ||||||
|  | 		{ | ||||||
|  | 			'txt': "%%%s%%" % txt, | ||||||
|  | 			'_txt': txt.replace("%", ""), | ||||||
|  | 			'start': start, | ||||||
|  | 			'page_len': page_len, | ||||||
|  | 			'account': account | ||||||
|  | 		} | ||||||
|  | 	) | ||||||
|  | 
 | ||||||
|  | def sales_invoices_query(doctype, txt, searchfield, start, page_len, filters): | ||||||
|  | 	return frappe.db.sql(""" | ||||||
|  | 		SELECT | ||||||
|  | 			sip.parent, si.customer, sip.amount, sip.mode_of_payment | ||||||
|  | 		FROM | ||||||
|  | 			`tabSales Invoice Payment` as sip | ||||||
|  | 		LEFT JOIN | ||||||
|  | 			`tabSales Invoice` as si | ||||||
|  | 		ON | ||||||
|  | 			sip.parent = si.name | ||||||
|  | 		WHERE | ||||||
|  | 			(sip.clearance_date is null or sip.clearance_date='0000-00-00') | ||||||
|  | 		AND | ||||||
|  | 			(sip.parent like %(txt)s or si.customer like %(txt)s) | ||||||
|  | 		ORDER BY | ||||||
|  | 			if(locate(%(_txt)s, sip.parent), locate(%(_txt)s, sip.parent), 99999), | ||||||
|  | 			sip.parent | ||||||
|  | 		LIMIT | ||||||
|  | 			%(start)s, %(page_len)s""", | ||||||
|  | 		{ | ||||||
|  | 			'txt': "%%%s%%" % txt, | ||||||
|  | 			'_txt': txt.replace("%", ""), | ||||||
|  | 			'start': start, | ||||||
|  | 			'page_len': page_len | ||||||
|  | 		} | ||||||
|  | 	) | ||||||
| @ -0,0 +1,21 @@ | |||||||
|  | <div class="transaction-header"> | ||||||
|  | 	<div class="level list-row list-row-head text-muted small"> | ||||||
|  | 		<div class="col-sm-2 ellipsis hidden-xs"> | ||||||
|  | 			{{ __("Date") }} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-xs-11 col-sm-4 ellipsis list-subject"> | ||||||
|  | 			{{ __("Description") }} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-sm-2 ellipsis hidden-xs"> | ||||||
|  | 			{{ __("Debit") }} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-sm-2 ellipsis hidden-xs"> | ||||||
|  | 			{{ __("Credit") }} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-sm-1 ellipsis hidden-xs"> | ||||||
|  | 			{{ __("Currency") }} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-sm-1 ellipsis"> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  | </div> | ||||||
| @ -0,0 +1,36 @@ | |||||||
|  | <div class="list-row transaction-item"> | ||||||
|  | 	<div> | ||||||
|  | 		<div class="clickable-section" data-name={{ name }}> | ||||||
|  | 			<div class="col-sm-2 ellipsis hidden-xs"> | ||||||
|  | 				{%= frappe.datetime.str_to_user(date) %} | ||||||
|  | 			</div> | ||||||
|  | 			<div class="col-xs-8 col-sm-4 ellipsis list-subject"> | ||||||
|  | 				{{ description }} | ||||||
|  | 			</div> | ||||||
|  | 			<div class="col-sm-2 ellipsis hidden-xs"> | ||||||
|  | 				{%= format_currency(debit, currency) %} | ||||||
|  | 			</div> | ||||||
|  | 			<div class="col-sm-2 ellipsis hidden-xs"> | ||||||
|  | 				{%= format_currency(credit, currency) %} | ||||||
|  | 			</div> | ||||||
|  | 			<div class="col-sm-1 ellipsis hidden-xs"> | ||||||
|  | 				{{ currency }} | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-xs-3 col-sm-1"> | ||||||
|  | 			<div class="btn-group"> | ||||||
|  | 				<a class="dropdown-toggle btn btn-default btn-xs" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> | ||||||
|  | 					<span>Actions </span> | ||||||
|  | 					<span class="caret"></span> | ||||||
|  | 				</a> | ||||||
|  | 				<ul class="dropdown-menu reports-dropdown" style="max-height: 300px; overflow-y: auto; right: 0px; left: auto;"> | ||||||
|  | 					<li><a class="new-reconciliation" data-name={{ name }}>{{ __("Reconcile") }}</a></li> | ||||||
|  | 					<li class="divider"></li> | ||||||
|  | 					<li><a class="new-payment" data-name={{ name }}>{{ __("New Payment") }}</a></li> | ||||||
|  | 					<li><a class="new-invoice" data-name={{ name }}>{{ __("New Invoice") }}</a></li> | ||||||
|  | 					<li><a class="new-expense" data-name={{ name }}>{{ __("New Expense") }}</a></li> | ||||||
|  | 				</ul> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  | </div> | ||||||
| @ -0,0 +1,21 @@ | |||||||
|  | <div class="transaction-header"> | ||||||
|  | 	<div class="level list-row list-row-head text-muted small"> | ||||||
|  | 		<div class="col-xs-3 col-sm-2 ellipsis"> | ||||||
|  | 			{{ __("Payment Name") }} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-xs-3 col-sm-2 ellipsis"> | ||||||
|  | 			{{ __("Reference Date") }} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-sm-2 ellipsis hidden-xs"> | ||||||
|  | 			{{ __("Amount") }} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-sm-2 ellipsis hidden-xs"> | ||||||
|  | 			{{ __("Party") }} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-xs-3 col-sm-2 ellipsis"> | ||||||
|  | 			{{ __("Reference Number") }} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-xs-2 col-sm-2"> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  | </div> | ||||||
| @ -0,0 +1,36 @@ | |||||||
|  | <div class="list-row"> | ||||||
|  | 	<div> | ||||||
|  | 		<div class="col-xs-3 col-sm-2 ellipsis"> | ||||||
|  | 			{{ name }} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-xs-3 col-sm-2 ellipsis"> | ||||||
|  | 			{% if (typeof reference_date !== "undefined") %} | ||||||
|  | 				{%= frappe.datetime.str_to_user(reference_date) %} | ||||||
|  | 			{% else %} | ||||||
|  | 				{% if (typeof posting_date !== "undefined") %} | ||||||
|  | 					{%= frappe.datetime.str_to_user(posting_date) %} | ||||||
|  | 				{% endif %} | ||||||
|  | 			{% endif %} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-sm-2 ellipsis hidden-xs"> | ||||||
|  | 			{{ format_currency(paid_amount, currency) }} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-sm-2 ellipsis hidden-xs"> | ||||||
|  | 			{% if (typeof party !== "undefined") %} | ||||||
|  | 				{{ party }} | ||||||
|  | 			{% endif %} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-xs-3 col-sm-2 ellipsis"> | ||||||
|  | 			{% if (typeof reference_no !== "undefined") %} | ||||||
|  | 				{{ reference_no }} | ||||||
|  | 			{% else %} | ||||||
|  | 				{{ "" }} | ||||||
|  | 			{% endif %} | ||||||
|  | 		</div> | ||||||
|  | 		<div class="col-xs-2 col-sm-2"> | ||||||
|  | 			<div class="text-right margin-bottom"> | ||||||
|  | 				<button class="btn btn-primary btn-xs reconciliation-btn" data-doctype="{{ doctype }}" data-name="{{ name }}">{{ __("Reconcile") }}</button> | ||||||
|  | 			</div> | ||||||
|  | 		</div> | ||||||
|  | 	</div> | ||||||
|  | </div> | ||||||
| @ -1957,6 +1957,12 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ | |||||||
| 			}], | 			}], | ||||||
| 			function(values){ | 			function(values){ | ||||||
| 				me.item_batch_no[me.items[0].item_code] = values.batch; | 				me.item_batch_no[me.items[0].item_code] = values.batch; | ||||||
|  | 				const item = me.frm.doc.items.find( | ||||||
|  | 					({ item_code }) => item_code === me.items[0].item_code | ||||||
|  | 				); | ||||||
|  | 				if (item) { | ||||||
|  | 					item.batch_no = values.batch; | ||||||
|  | 				} | ||||||
| 			}, | 			}, | ||||||
| 			__('Select Batch No')) | 			__('Select Batch No')) | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -44,7 +44,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company= | |||||||
| 		frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError) | 		frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError) | ||||||
| 
 | 
 | ||||||
| 	party = frappe.get_doc(party_type, party) | 	party = frappe.get_doc(party_type, party) | ||||||
| 	currency = party.default_currency if party.default_currency else get_company_currency(company) | 	currency = party.default_currency if party.get("default_currency") else get_company_currency(company) | ||||||
| 
 | 
 | ||||||
| 	party_address, shipping_address = set_address_details(out, party, party_type, doctype, company, party_address, shipping_address) | 	party_address, shipping_address = set_address_details(out, party, party_type, doctype, company, party_address, shipping_address) | ||||||
| 	set_contact_details(out, party, party_type) | 	set_contact_details(out, party, party_type) | ||||||
| @ -144,7 +144,7 @@ def set_other_values(out, party, party_type): | |||||||
| 
 | 
 | ||||||
| def get_default_price_list(party): | def get_default_price_list(party): | ||||||
| 	"""Return default price list for party (Document object)""" | 	"""Return default price list for party (Document object)""" | ||||||
| 	if party.default_price_list: | 	if party.get("default_price_list"): | ||||||
| 		return party.default_price_list | 		return party.default_price_list | ||||||
| 
 | 
 | ||||||
| 	if party.doctype == "Customer": | 	if party.doctype == "Customer": | ||||||
|  | |||||||
| @ -1,19 +1,23 @@ | |||||||
| { | { | ||||||
|  "creation": "2014-08-28 11:11:39.796473",  |  "align_labels_right": 0, | ||||||
|  "custom_format": 0,  |  "creation": "2014-08-28 11:11:39.796473", | ||||||
|  "disabled": 0,  |  "custom_format": 0, | ||||||
|  "doc_type": "Journal Entry",  |  "disabled": 0, | ||||||
|  "docstatus": 0,  |  "doc_type": "Journal Entry", | ||||||
|  "doctype": "Print Format",  |  "docstatus": 0, | ||||||
|  "html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n\n<div class=\"page-break\">\n    {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n        and doc.set(\"select_print_heading\", _(\"Credit Note\")) -%}{%- endif -%}\n    {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n    {%- for label, value in (\n        (_(\"Credit To\"), doc.pay_to_recd_from),\n        (_(\"Date\"), frappe.utils.formatdate(doc.voucher_date)),\n        (_(\"Amount\"), \"<strong>\" + doc.get_formatted(\"total_amount\") + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n        (_(\"Remarks\"), doc.remark)\n    ) -%}\n\n    <div class=\"row\">\n        <div class=\"col-xs-3\"><label class=\"text-right\">{{ label }}</label></div>\n        <div class=\"col-xs-9\">{{ value }}</div>\n    </div>\n\n    {%- endfor -%}\n\n    <hr>\n    <br>\n    <p class=\"strong\">\n        {{ _(\"For\") }} {{ doc.company }},<br>\n        <br>\n        <br>\n        <br>\n        {{ _(\"Authorized Signatory\") }}\n    </p>\n</div>\n\n\n",  |  "doctype": "Print Format", | ||||||
|  "idx": 2,  |  "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"<div class=\\\"print-heading\\\">\\t\\t\\t\\t<h2>Journal Entry<br><small>{{ doc.name }}</small>\\t\\t\\t\\t</h2></div>\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"voucher_type\", \"print_hide\": 0, \"label\": \"Entry Type\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"posting_date\", \"print_hide\": 0, \"label\": \"Posting Date\"}, {\"fieldname\": \"finance_book\", \"print_hide\": 0, \"label\": \"Finance Book\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"accounts\", \"print_hide\": 0, \"label\": \"Accounting Entries\", \"visible_columns\": [{\"fieldname\": \"account\", \"print_width\": \"250px\", \"print_hide\": 0}, {\"fieldname\": \"bank_account_no\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"party_type\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"party\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"debit_in_account_currency\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"credit_in_account_currency\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"reference_type\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"reference_name\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"reference_due_date\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"project\", \"print_width\": \"\", \"print_hide\": 0}]}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"cheque_no\", \"print_hide\": 0, \"label\": \"Reference Number\"}, {\"fieldname\": \"cheque_date\", \"print_hide\": 0, \"label\": \"Reference Date\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"get_balance\", \"print_hide\": 0, \"label\": \"Make Difference Entry\"}, {\"fieldname\": \"total_amount\", \"print_hide\": 0, \"label\": \"Total Amount\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Reference\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"clearance_date\", \"print_hide\": 0, \"label\": \"Clearance Date\"}, {\"fieldname\": \"remark\", \"print_hide\": 0, \"label\": \"Remark\"}, {\"fieldname\": \"inter_company_journal_entry_reference\", \"print_hide\": 0, \"label\": \"Inter Company Journal Entry Reference\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"due_date\", \"print_hide\": 0, \"label\": \"Due Date\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Printing Settings\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"pay_to_recd_from\", \"print_hide\": 0, \"label\": \"Pay To / Recd From\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"letter_head\", \"print_hide\": 0, \"label\": \"Letter Head\"}, {\"fieldtype\": \"Section Break\", \"label\": \"More Information\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"mode_of_payment\", \"print_hide\": 0, \"label\": \"Mode of Payment\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"stock_entry\", \"print_hide\": 0, \"label\": \"Stock Entry\"}]", | ||||||
|  "modified": "2015-07-22 17:42:01.560817",  |  "html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n\n<div class=\"page-break\">\n    {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n        and doc.set(\"select_print_heading\", _(\"Credit Note\")) -%}{%- endif -%}\n    {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n    {%- for label, value in (\n        (_(\"Credit To\"), doc.pay_to_recd_from),\n        (_(\"Date\"), frappe.utils.formatdate(doc.voucher_date)),\n        (_(\"Amount\"), \"<strong>\" + doc.get_formatted(\"total_amount\") + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n        (_(\"Remarks\"), doc.remark)\n    ) -%}\n\n    <div class=\"row\">\n        <div class=\"col-xs-3\"><label class=\"text-right\">{{ label }}</label></div>\n        <div class=\"col-xs-9\">{{ value }}</div>\n    </div>\n\n    {%- endfor -%}\n\n    <hr>\n    <br>\n    <p class=\"strong\">\n        {{ _(\"For\") }} {{ doc.company }},<br>\n        <br>\n        <br>\n        <br>\n        {{ _(\"Authorized Signatory\") }}\n    </p>\n</div>\n\n\n", | ||||||
|  "modified_by": "Administrator",  |  "idx": 2, | ||||||
|  "name": "Credit Note",  |  "line_breaks": 0, | ||||||
|  "owner": "Administrator",  |  "modified": "2019-04-18 12:10:14.732269", | ||||||
|  "parent": "Journal Entry",  |  "modified_by": "Administrator", | ||||||
|  "parentfield": "__print_formats",  |  "module": "Accounts", | ||||||
|  "parenttype": "DocType",  |  "name": "Credit Note", | ||||||
|  "print_format_type": "Server",  |  "owner": "Administrator", | ||||||
|  |  "parentfield": "__print_formats", | ||||||
|  |  "print_format_builder": 0, | ||||||
|  |  "print_format_type": "Server", | ||||||
|  |  "show_section_headings": 0, | ||||||
|  "standard": "Yes" |  "standard": "Yes" | ||||||
| } | } | ||||||
| @ -197,10 +197,9 @@ class ReceivablePayableReport(object): | |||||||
| 			self.payment_term_map = self.get_payment_term_detail(voucher_nos) | 			self.payment_term_map = self.get_payment_term_detail(voucher_nos) | ||||||
| 
 | 
 | ||||||
| 		for gle in gl_entries_data: | 		for gle in gl_entries_data: | ||||||
| 			if self.is_receivable_or_payable(gle, self.dr_or_cr, future_vouchers): | 			if self.is_receivable_or_payable(gle, self.dr_or_cr, future_vouchers, return_entries): | ||||||
| 				outstanding_amount, credit_note_amount, payment_amount = self.get_outstanding_amount( | 				outstanding_amount, credit_note_amount, payment_amount = self.get_outstanding_amount( | ||||||
| 					gle,self.filters.report_date, self.dr_or_cr, return_entries) | 					gle,self.filters.report_date, self.dr_or_cr, return_entries) | ||||||
| 
 |  | ||||||
| 				temp_outstanding_amt = outstanding_amount | 				temp_outstanding_amt = outstanding_amount | ||||||
| 				temp_credit_note_amt = credit_note_amount | 				temp_credit_note_amt = credit_note_amount | ||||||
| 
 | 
 | ||||||
| @ -379,7 +378,7 @@ class ReceivablePayableReport(object): | |||||||
| 		# returns a generator | 		# returns a generator | ||||||
| 		return self.get_gl_entries(party_type, report_date) | 		return self.get_gl_entries(party_type, report_date) | ||||||
| 
 | 
 | ||||||
| 	def is_receivable_or_payable(self, gle, dr_or_cr, future_vouchers): | 	def is_receivable_or_payable(self, gle, dr_or_cr, future_vouchers, return_entries): | ||||||
| 		return ( | 		return ( | ||||||
| 			# advance | 			# advance | ||||||
| 			(not gle.against_voucher) or | 			(not gle.against_voucher) or | ||||||
| @ -390,30 +389,37 @@ class ReceivablePayableReport(object): | |||||||
| 			# sales invoice/purchase invoice | 			# sales invoice/purchase invoice | ||||||
| 			(gle.against_voucher==gle.voucher_no and gle.get(dr_or_cr) > 0) or | 			(gle.against_voucher==gle.voucher_no and gle.get(dr_or_cr) > 0) or | ||||||
| 
 | 
 | ||||||
|  | 			# standalone credit notes | ||||||
|  | 			(gle.against_voucher==gle.voucher_no and gle.voucher_no in return_entries and not return_entries.get(gle.voucher_no)) or | ||||||
|  | 
 | ||||||
| 			# entries adjusted with future vouchers | 			# entries adjusted with future vouchers | ||||||
| 			((gle.against_voucher_type, gle.against_voucher) in future_vouchers) | 			((gle.against_voucher_type, gle.against_voucher) in future_vouchers) | ||||||
| 		) | 		) | ||||||
| 
 | 
 | ||||||
| 	def get_return_entries(self, party_type): | 	def get_return_entries(self, party_type): | ||||||
| 		doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice" | 		doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice" | ||||||
| 		return [d.name for d in frappe.get_all(doctype, filters={"is_return": 1, "docstatus": 1})] | 		return_entries = frappe._dict(frappe.get_all(doctype, | ||||||
|  | 			filters={"is_return": 1, "docstatus": 1}, fields=["name", "return_against"], as_list=1)) | ||||||
|  | 		return return_entries | ||||||
| 
 | 
 | ||||||
| 	def get_outstanding_amount(self, gle, report_date, dr_or_cr, return_entries): | 	def get_outstanding_amount(self, gle, report_date, dr_or_cr, return_entries): | ||||||
| 		payment_amount, credit_note_amount = 0.0, 0.0 | 		payment_amount, credit_note_amount = 0.0, 0.0 | ||||||
| 		reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit" | 		reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit" | ||||||
| 
 |  | ||||||
| 		for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no): | 		for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no): | ||||||
| 			if getdate(e.posting_date) <= report_date and e.name!=gle.name: | 			if getdate(e.posting_date) <= report_date \ | ||||||
|  | 				and (e.name!=gle.name or (e.voucher_no in return_entries and not return_entries.get(e.voucher_no))): | ||||||
|  | 
 | ||||||
| 				amount = flt(e.get(reverse_dr_or_cr), self.currency_precision) - flt(e.get(dr_or_cr), self.currency_precision) | 				amount = flt(e.get(reverse_dr_or_cr), self.currency_precision) - flt(e.get(dr_or_cr), self.currency_precision) | ||||||
| 				if e.voucher_no not in return_entries: | 				if e.voucher_no not in return_entries: | ||||||
| 					payment_amount += amount | 					payment_amount += amount | ||||||
| 				else: | 				else: | ||||||
| 					credit_note_amount += amount | 					credit_note_amount += amount | ||||||
| 
 | 
 | ||||||
| 		outstanding_amount = (flt((flt(gle.get(dr_or_cr), self.currency_precision) | 		voucher_amount = flt(gle.get(dr_or_cr), self.currency_precision) - flt(gle.get(reverse_dr_or_cr), self.currency_precision) | ||||||
| 			- flt(gle.get(reverse_dr_or_cr), self.currency_precision) | 		if gle.voucher_no in return_entries and not return_entries.get(gle.voucher_no): | ||||||
| 			- payment_amount - credit_note_amount), self.currency_precision)) | 			voucher_amount = 0 | ||||||
| 
 | 
 | ||||||
|  | 		outstanding_amount = flt((voucher_amount - payment_amount - credit_note_amount), self.currency_precision) | ||||||
| 		credit_note_amount = flt(credit_note_amount, self.currency_precision) | 		credit_note_amount = flt(credit_note_amount, self.currency_precision) | ||||||
| 
 | 
 | ||||||
| 		return outstanding_amount, credit_note_amount, payment_amount | 		return outstanding_amount, credit_note_amount, payment_amount | ||||||
|  | |||||||
| @ -322,7 +322,10 @@ def sort_accounts(accounts, is_root=False, key="name"): | |||||||
| 	"""Sort root types as Asset, Liability, Equity, Income, Expense""" | 	"""Sort root types as Asset, Liability, Equity, Income, Expense""" | ||||||
| 
 | 
 | ||||||
| 	def compare_accounts(a, b): | 	def compare_accounts(a, b): | ||||||
| 		if is_root: | 		if re.split('\W+', a[key])[0].isdigit(): | ||||||
|  | 			# if chart of accounts is numbered, then sort by number | ||||||
|  | 			return cmp(a[key], b[key]) | ||||||
|  | 		elif is_root: | ||||||
| 			if a.report_type != b.report_type and a.report_type == "Balance Sheet": | 			if a.report_type != b.report_type and a.report_type == "Balance Sheet": | ||||||
| 				return -1 | 				return -1 | ||||||
| 			if a.root_type != b.root_type and a.root_type == "Asset": | 			if a.root_type != b.root_type and a.root_type == "Asset": | ||||||
| @ -353,6 +356,7 @@ def set_gl_entries_by_account( | |||||||
| 		"company": company, | 		"company": company, | ||||||
| 		"from_date": from_date, | 		"from_date": from_date, | ||||||
| 		"to_date": to_date, | 		"to_date": to_date, | ||||||
|  | 		"finance_book": filters.get("finance_book") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if filters.get("include_default_book_entries"): | 	if filters.get("include_default_book_entries"): | ||||||
|  | |||||||
| @ -171,4 +171,3 @@ dimension_filters.then((dimensions) => { | |||||||
| 		}); | 		}); | ||||||
| 	}); | 	}); | ||||||
| }); | }); | ||||||
| 
 |  | ||||||
|  | |||||||
| @ -65,7 +65,7 @@ def get_columns(group_wise_columns, filters): | |||||||
| 		"warehouse": _("Warehouse") + ":Link/Warehouse", | 		"warehouse": _("Warehouse") + ":Link/Warehouse", | ||||||
| 		"qty": _("Qty") + ":Float", | 		"qty": _("Qty") + ":Float", | ||||||
| 		"base_rate": _("Avg. Selling Rate") + ":Currency/currency", | 		"base_rate": _("Avg. Selling Rate") + ":Currency/currency", | ||||||
| 		"buying_rate": _("Avg. Buying Rate") + ":Currency/currency", | 		"buying_rate": _("Valuation Rate") + ":Currency/currency", | ||||||
| 		"base_amount": _("Selling Amount") + ":Currency/currency", | 		"base_amount": _("Selling Amount") + ":Currency/currency", | ||||||
| 		"buying_amount": _("Buying Amount") + ":Currency/currency", | 		"buying_amount": _("Buying Amount") + ":Currency/currency", | ||||||
| 		"gross_profit": _("Gross Profit") + ":Currency/currency", | 		"gross_profit": _("Gross Profit") + ":Currency/currency", | ||||||
|  | |||||||
| @ -1,14 +1,13 @@ | |||||||
| { | { | ||||||
|  "add_total_row": 0, |  "add_total_row": 0, | ||||||
|  "creation": "2019-05-01 13:46:23.044979", |  "creation": "2019-05-01 12:59:52.018850", | ||||||
|  "disable_prepared_report": 0, |  "disable_prepared_report": 0, | ||||||
|  "disabled": 0, |  "disabled": 0, | ||||||
|  "docstatus": 0, |  "docstatus": 0, | ||||||
|  "doctype": "Report", |  "doctype": "Report", | ||||||
|  "idx": 0, |  "idx": 0, | ||||||
|  "is_standard": "Yes", |  "is_standard": "Yes", | ||||||
|  "letter_head": "Test Letter Head 1", |  "modified": "2019-05-01 13:00:26.545278", | ||||||
|  "modified": "2019-05-01 13:46:23.044979", |  | ||||||
|  "modified_by": "Administrator", |  "modified_by": "Administrator", | ||||||
|  "module": "Accounts", |  "module": "Accounts", | ||||||
|  "name": "Inactive Sales Items", |  "name": "Inactive Sales Items", | ||||||
| @ -17,5 +16,15 @@ | |||||||
|  "ref_doctype": "Sales Invoice", |  "ref_doctype": "Sales Invoice", | ||||||
|  "report_name": "Inactive Sales Items", |  "report_name": "Inactive Sales Items", | ||||||
|  "report_type": "Script Report", |  "report_type": "Script Report", | ||||||
|  "roles": [] |  "roles": [ | ||||||
|  |   { | ||||||
|  |    "role": "Accounts User" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "role": "Accounts Manager" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "role": "Auditor" | ||||||
|  |   } | ||||||
|  |  ] | ||||||
| } | } | ||||||
| @ -102,9 +102,7 @@ def get_conditions(filters): | |||||||
| 		("customer", " and `tabSales Invoice`.customer = %(customer)s"), | 		("customer", " and `tabSales Invoice`.customer = %(customer)s"), | ||||||
| 		("item_code", " and `tabSales Invoice Item`.item_code = %(item_code)s"), | 		("item_code", " and `tabSales Invoice Item`.item_code = %(item_code)s"), | ||||||
| 		("from_date", " and `tabSales Invoice`.posting_date>=%(from_date)s"), | 		("from_date", " and `tabSales Invoice`.posting_date>=%(from_date)s"), | ||||||
| 		("to_date", " and `tabSales Invoice`.posting_date<=%(to_date)s"), | 		("to_date", " and `tabSales Invoice`.posting_date<=%(to_date)s")): | ||||||
| 		("company_gstin", " and `tabSales Invoice`.company_gstin = %(company_gstin)s"), |  | ||||||
| 		("invoice_type", " and `tabSales Invoice`.invoice_type = %(invoice_type)s")): |  | ||||||
| 			if filters.get(opts[0]): | 			if filters.get(opts[0]): | ||||||
| 				conditions += opts[1] | 				conditions += opts[1] | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -157,7 +157,7 @@ def get_conditions(filters): | |||||||
| 		conditions +=  """ and exists(select name from `tabSales Invoice Item` | 		conditions +=  """ and exists(select name from `tabSales Invoice Item` | ||||||
| 			 where parent=`tabSales Invoice`.name | 			 where parent=`tabSales Invoice`.name | ||||||
| 			 	and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s)""" | 			 	and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s)""" | ||||||
| 	 | 
 | ||||||
| 	if filters.get("item_group"): | 	if filters.get("item_group"): | ||||||
| 		conditions +=  """ and exists(select name from `tabSales Invoice Item` | 		conditions +=  """ and exists(select name from `tabSales Invoice Item` | ||||||
| 			 where parent=`tabSales Invoice`.name | 			 where parent=`tabSales Invoice`.name | ||||||
| @ -171,7 +171,7 @@ def get_invoices(filters, additional_query_columns): | |||||||
| 
 | 
 | ||||||
| 	conditions = get_conditions(filters) | 	conditions = get_conditions(filters) | ||||||
| 	return frappe.db.sql(""" | 	return frappe.db.sql(""" | ||||||
| 		select name, posting_date, debit_to, project, customer,  | 		select name, posting_date, debit_to, project, customer, | ||||||
| 		customer_name, owner, remarks, territory, tax_id, customer_group, | 		customer_name, owner, remarks, territory, tax_id, customer_group, | ||||||
| 		base_net_total, base_grand_total, base_rounded_total, outstanding_amount {0} | 		base_net_total, base_grand_total, base_rounded_total, outstanding_amount {0} | ||||||
| 		from `tabSales Invoice` | 		from `tabSales Invoice` | ||||||
|  | |||||||
| @ -618,7 +618,7 @@ def get_held_invoices(party_type, party): | |||||||
| 	return held_invoices | 	return held_invoices | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_outstanding_invoices(party_type, party, account, condition=None, limit=None): | def get_outstanding_invoices(party_type, party, account, condition=None): | ||||||
| 	outstanding_invoices = [] | 	outstanding_invoices = [] | ||||||
| 	precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2 | 	precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2 | ||||||
| 
 | 
 | ||||||
| @ -631,7 +631,6 @@ def get_outstanding_invoices(party_type, party, account, condition=None, limit=N | |||||||
| 
 | 
 | ||||||
| 	invoice = 'Sales Invoice' if erpnext.get_party_account_type(party_type) == 'Receivable' else 'Purchase Invoice' | 	invoice = 'Sales Invoice' if erpnext.get_party_account_type(party_type) == 'Receivable' else 'Purchase Invoice' | ||||||
| 	held_invoices = get_held_invoices(party_type, party) | 	held_invoices = get_held_invoices(party_type, party) | ||||||
| 	limit_cond = "limit %s" % limit if limit else "" |  | ||||||
| 
 | 
 | ||||||
| 	invoice_list = frappe.db.sql(""" | 	invoice_list = frappe.db.sql(""" | ||||||
| 		select | 		select | ||||||
| @ -646,11 +645,10 @@ def get_outstanding_invoices(party_type, party, account, condition=None, limit=N | |||||||
| 					and (against_voucher = '' or against_voucher is null)) | 					and (against_voucher = '' or against_voucher is null)) | ||||||
| 				or (voucher_type not in ('Journal Entry', 'Payment Entry'))) | 				or (voucher_type not in ('Journal Entry', 'Payment Entry'))) | ||||||
| 		group by voucher_type, voucher_no | 		group by voucher_type, voucher_no | ||||||
| 		order by posting_date, name {limit_cond}""".format( | 		order by posting_date, name""".format( | ||||||
| 			dr_or_cr=dr_or_cr, | 			dr_or_cr=dr_or_cr, | ||||||
| 			invoice = invoice, | 			invoice = invoice, | ||||||
| 			condition=condition or "", | 			condition=condition or "" | ||||||
| 			limit_cond = limit_cond |  | ||||||
| 		), { | 		), { | ||||||
| 			"party_type": party_type, | 			"party_type": party_type, | ||||||
| 			"party": party, | 			"party": party, | ||||||
|  | |||||||
| @ -1,497 +1,497 @@ | |||||||
| { | { | ||||||
|  "allow_import": 1, |    "allow_import": 1, | ||||||
|  "allow_rename": 1, |    "allow_rename": 1, | ||||||
|  "autoname": "naming_series:", |    "autoname": "naming_series:", | ||||||
|  "creation": "2016-03-01 17:01:27.920130", |    "creation": "2016-03-01 17:01:27.920130", | ||||||
|  "doctype": "DocType", |    "doctype": "DocType", | ||||||
|  "document_type": "Document", |    "document_type": "Document", | ||||||
|  "field_order": [ |    "field_order": [ | ||||||
|   "naming_series", |     "naming_series", | ||||||
|   "asset_name", |     "asset_name", | ||||||
|   "item_code", |     "item_code", | ||||||
|   "item_name", |     "item_name", | ||||||
|   "asset_category", |     "asset_category", | ||||||
|   "asset_owner", |     "asset_owner", | ||||||
|   "asset_owner_company", |     "asset_owner_company", | ||||||
|   "supplier", |     "supplier", | ||||||
|   "customer", |     "customer", | ||||||
|   "image", |     "image", | ||||||
|   "column_break_3", |     "column_break_3", | ||||||
|   "company", |     "company", | ||||||
|   "location", |     "location", | ||||||
|   "custodian", |     "custodian", | ||||||
|   "department", |     "department", | ||||||
|   "purchase_date", |     "purchase_date", | ||||||
|   "disposal_date", |     "disposal_date", | ||||||
|   "journal_entry_for_scrap", |     "journal_entry_for_scrap", | ||||||
|   "accounting_dimensions_section", |     "accounting_dimensions_section", | ||||||
|   "cost_center", |     "cost_center", | ||||||
|   "dimension_col_break", |     "dimension_col_break", | ||||||
|   "section_break_5", |     "section_break_5", | ||||||
|   "gross_purchase_amount", |     "gross_purchase_amount", | ||||||
|   "available_for_use_date", |     "available_for_use_date", | ||||||
|   "column_break_18", |     "column_break_18", | ||||||
|   "calculate_depreciation", |     "calculate_depreciation", | ||||||
|   "is_existing_asset", |     "is_existing_asset", | ||||||
|   "opening_accumulated_depreciation", |     "opening_accumulated_depreciation", | ||||||
|   "number_of_depreciations_booked", |     "number_of_depreciations_booked", | ||||||
|   "section_break_23", |     "section_break_23", | ||||||
|   "finance_books", |     "finance_books", | ||||||
|   "section_break_33", |     "section_break_33", | ||||||
|   "depreciation_method", |     "depreciation_method", | ||||||
|   "value_after_depreciation", |     "value_after_depreciation", | ||||||
|   "total_number_of_depreciations", |     "total_number_of_depreciations", | ||||||
|   "column_break_24", |     "column_break_24", | ||||||
|   "frequency_of_depreciation", |     "frequency_of_depreciation", | ||||||
|   "next_depreciation_date", |     "next_depreciation_date", | ||||||
|   "section_break_14", |     "section_break_14", | ||||||
|   "schedules", |     "schedules", | ||||||
|   "insurance_details", |     "insurance_details", | ||||||
|   "policy_number", |     "policy_number", | ||||||
|   "insurer", |     "insurer", | ||||||
|   "insured_value", |     "insured_value", | ||||||
|   "column_break_48", |     "column_break_48", | ||||||
|   "insurance_start_date", |     "insurance_start_date", | ||||||
|   "insurance_end_date", |     "insurance_end_date", | ||||||
|   "comprehensive_insurance", |     "comprehensive_insurance", | ||||||
|   "section_break_31", |     "section_break_31", | ||||||
|   "maintenance_required", |     "maintenance_required", | ||||||
|   "other_details", |     "other_details", | ||||||
|   "status", |     "status", | ||||||
|   "booked_fixed_asset", |     "booked_fixed_asset", | ||||||
|   "column_break_51", |     "column_break_51", | ||||||
|   "purchase_receipt", |     "purchase_receipt", | ||||||
|   "purchase_receipt_amount", |     "purchase_receipt_amount", | ||||||
|   "purchase_invoice", |     "purchase_invoice", | ||||||
|   "default_finance_book", |     "default_finance_book", | ||||||
|   "amended_from" |     "amended_from" | ||||||
|  ], |    ], | ||||||
|  "fields": [ |    "fields": [ | ||||||
|   { |     { | ||||||
|    "fieldname": "naming_series", |      "fieldname": "naming_series", | ||||||
|    "fieldtype": "Select", |      "fieldtype": "Select", | ||||||
|    "label": "Naming Series", |      "label": "Naming Series", | ||||||
|    "options": "ACC-ASS-.YYYY.-" |      "options": "ACC-ASS-.YYYY.-" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "asset_name", |      "fieldname": "asset_name", | ||||||
|    "fieldtype": "Data", |      "fieldtype": "Data", | ||||||
|    "in_list_view": 1, |      "in_list_view": 1, | ||||||
|    "label": "Asset Name", |      "label": "Asset Name", | ||||||
|    "reqd": 1 |      "reqd": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "item_code", |      "fieldname": "item_code", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "in_standard_filter": 1, |      "in_standard_filter": 1, | ||||||
|    "label": "Item Code", |      "label": "Item Code", | ||||||
|    "options": "Item", |      "options": "Item", | ||||||
|    "reqd": 1 |      "reqd": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fetch_from": "item_code.item_name", |      "fetch_from": "item_code.item_name", | ||||||
|    "fieldname": "item_name", |      "fieldname": "item_name", | ||||||
|    "fieldtype": "Read Only", |      "fieldtype": "Read Only", | ||||||
|    "label": "Item Name" |      "label": "Item Name" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fetch_from": "item_code.asset_category", |      "fetch_from": "item_code.asset_category", | ||||||
|    "fieldname": "asset_category", |      "fieldname": "asset_category", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "in_global_search": 1, |      "in_global_search": 1, | ||||||
|    "in_list_view": 1, |      "in_list_view": 1, | ||||||
|    "in_standard_filter": 1, |      "in_standard_filter": 1, | ||||||
|    "label": "Asset Category", |      "label": "Asset Category", | ||||||
|    "options": "Asset Category", |      "options": "Asset Category", | ||||||
|    "read_only": 1 |      "read_only": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "asset_owner", |      "fieldname": "asset_owner", | ||||||
|    "fieldtype": "Select", |      "fieldtype": "Select", | ||||||
|    "label": "Asset Owner", |      "label": "Asset Owner", | ||||||
|    "options": "\nCompany\nSupplier\nCustomer" |      "options": "\nCompany\nSupplier\nCustomer" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "depends_on": "eval:doc.asset_owner == \"Company\"", |      "depends_on": "eval:doc.asset_owner == \"Company\"", | ||||||
|    "fieldname": "asset_owner_company", |      "fieldname": "asset_owner_company", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "label": "Asset Owner Company", |      "label": "Asset Owner Company", | ||||||
|    "options": "Company" |      "options": "Company" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "depends_on": "eval:doc.asset_owner == \"Supplier\"", |      "depends_on": "eval:doc.asset_owner == \"Supplier\"", | ||||||
|    "fieldname": "supplier", |      "fieldname": "supplier", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "label": "Supplier", |      "label": "Supplier", | ||||||
|    "options": "Supplier" |      "options": "Supplier" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "depends_on": "eval:doc.asset_owner == \"Customer\"", |      "depends_on": "eval:doc.asset_owner == \"Customer\"", | ||||||
|    "fieldname": "customer", |      "fieldname": "customer", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "label": "Customer", |      "label": "Customer", | ||||||
|    "options": "Customer" |      "options": "Customer" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "allow_on_submit": 1, |      "allow_on_submit": 1, | ||||||
|    "fieldname": "image", |      "fieldname": "image", | ||||||
|    "fieldtype": "Attach Image", |      "fieldtype": "Attach Image", | ||||||
|    "hidden": 1, |      "hidden": 1, | ||||||
|    "label": "Image", |      "label": "Image", | ||||||
|    "no_copy": 1, |      "no_copy": 1, | ||||||
|    "print_hide": 1 |      "print_hide": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "column_break_3", |      "fieldname": "column_break_3", | ||||||
|    "fieldtype": "Column Break" |      "fieldtype": "Column Break" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "company", |      "fieldname": "company", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "label": "Company", |      "label": "Company", | ||||||
|    "options": "Company", |      "options": "Company", | ||||||
|    "remember_last_selected_value": 1, |      "remember_last_selected_value": 1, | ||||||
|    "reqd": 1 |      "reqd": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "location", |      "fieldname": "location", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "in_list_view": 1, |      "in_list_view": 1, | ||||||
|    "label": "Location", |      "label": "Location", | ||||||
|    "options": "Location", |      "options": "Location", | ||||||
|    "reqd": 1 |      "reqd": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "custodian", |      "fieldname": "custodian", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "ignore_user_permissions": 1, |      "ignore_user_permissions": 1, | ||||||
|    "label": "Custodian", |      "label": "Custodian", | ||||||
|    "options": "Employee" |      "options": "Employee" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "cost_center", |      "fieldname": "cost_center", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "label": "Cost Center", |      "label": "Cost Center", | ||||||
|    "options": "Cost Center" |      "options": "Cost Center" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "department", |      "fieldname": "department", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "label": "Department", |      "label": "Department", | ||||||
|    "options": "Department" |      "options": "Department" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "purchase_date", |      "fieldname": "purchase_date", | ||||||
|    "fieldtype": "Date", |      "fieldtype": "Date", | ||||||
|    "label": "Purchase Date", |      "label": "Purchase Date", | ||||||
|    "reqd": 1 |      "reqd": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "disposal_date", |      "fieldname": "disposal_date", | ||||||
|    "fieldtype": "Date", |      "fieldtype": "Date", | ||||||
|    "label": "Disposal Date", |      "label": "Disposal Date", | ||||||
|    "read_only": 1 |      "read_only": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "journal_entry_for_scrap", |      "fieldname": "journal_entry_for_scrap", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "label": "Journal Entry for Scrap", |      "label": "Journal Entry for Scrap", | ||||||
|    "no_copy": 1, |      "no_copy": 1, | ||||||
|    "options": "Journal Entry", |      "options": "Journal Entry", | ||||||
|    "print_hide": 1, |      "print_hide": 1, | ||||||
|    "read_only": 1 |      "read_only": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "section_break_5", |      "fieldname": "section_break_5", | ||||||
|    "fieldtype": "Section Break" |      "fieldtype": "Section Break" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "gross_purchase_amount", |      "fieldname": "gross_purchase_amount", | ||||||
|    "fieldtype": "Currency", |      "fieldtype": "Currency", | ||||||
|    "label": "Gross Purchase Amount", |      "label": "Gross Purchase Amount", | ||||||
|    "options": "Company:company:default_currency", |      "options": "Company:company:default_currency", | ||||||
|    "reqd": 1 |      "reqd": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "available_for_use_date", |      "fieldname": "available_for_use_date", | ||||||
|    "fieldtype": "Date", |      "fieldtype": "Date", | ||||||
|    "label": "Available-for-use Date" |      "label": "Available-for-use Date" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "column_break_18", |      "fieldname": "column_break_18", | ||||||
|    "fieldtype": "Column Break" |      "fieldtype": "Column Break" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "default": "0", |      "default": "0", | ||||||
|    "fieldname": "calculate_depreciation", |      "fieldname": "calculate_depreciation", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Calculate Depreciation" |      "label": "Calculate Depreciation" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "default": "0", |      "default": "0", | ||||||
|    "fieldname": "is_existing_asset", |      "fieldname": "is_existing_asset", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Is Existing Asset" |      "label": "Is Existing Asset" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "depends_on": "is_existing_asset", |      "depends_on": "is_existing_asset", | ||||||
|    "fieldname": "opening_accumulated_depreciation", |      "fieldname": "opening_accumulated_depreciation", | ||||||
|    "fieldtype": "Currency", |      "fieldtype": "Currency", | ||||||
|    "label": "Opening Accumulated Depreciation", |      "label": "Opening Accumulated Depreciation", | ||||||
|    "no_copy": 1, |      "no_copy": 1, | ||||||
|    "options": "Company:company:default_currency" |      "options": "Company:company:default_currency" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "depends_on": "eval:(doc.is_existing_asset && doc.opening_accumulated_depreciation)", |      "depends_on": "eval:(doc.is_existing_asset && doc.opening_accumulated_depreciation)", | ||||||
|    "fieldname": "number_of_depreciations_booked", |      "fieldname": "number_of_depreciations_booked", | ||||||
|    "fieldtype": "Int", |      "fieldtype": "Int", | ||||||
|    "label": "Number of Depreciations Booked", |      "label": "Number of Depreciations Booked", | ||||||
|    "no_copy": 1 |      "no_copy": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "depends_on": "calculate_depreciation", |      "depends_on": "calculate_depreciation", | ||||||
|    "fieldname": "section_break_23", |      "fieldname": "section_break_23", | ||||||
|    "fieldtype": "Section Break", |      "fieldtype": "Section Break", | ||||||
|    "label": "Depreciation" |      "label": "Depreciation" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "finance_books", |      "fieldname": "finance_books", | ||||||
|    "fieldtype": "Table", |      "fieldtype": "Table", | ||||||
|    "label": "Finance Books", |      "label": "Finance Books", | ||||||
|    "options": "Asset Finance Book" |      "options": "Asset Finance Book" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "section_break_33", |      "fieldname": "section_break_33", | ||||||
|    "fieldtype": "Section Break", |      "fieldtype": "Section Break", | ||||||
|    "hidden": 1 |      "hidden": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "depreciation_method", |      "fieldname": "depreciation_method", | ||||||
|    "fieldtype": "Select", |      "fieldtype": "Select", | ||||||
|    "label": "Depreciation Method", |      "label": "Depreciation Method", | ||||||
|    "options": "\nStraight Line\nDouble Declining Balance\nManual" |      "options": "\nStraight Line\nDouble Declining Balance\nManual" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "value_after_depreciation", |      "fieldname": "value_after_depreciation", | ||||||
|    "fieldtype": "Currency", |      "fieldtype": "Currency", | ||||||
|    "hidden": 1, |      "hidden": 1, | ||||||
|    "label": "Value After Depreciation", |      "label": "Value After Depreciation", | ||||||
|    "options": "Company:company:default_currency", |      "options": "Company:company:default_currency", | ||||||
|    "read_only": 1 |      "read_only": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "total_number_of_depreciations", |      "fieldname": "total_number_of_depreciations", | ||||||
|    "fieldtype": "Int", |      "fieldtype": "Int", | ||||||
|    "label": "Total Number of Depreciations" |      "label": "Total Number of Depreciations" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "column_break_24", |      "fieldname": "column_break_24", | ||||||
|    "fieldtype": "Column Break" |      "fieldtype": "Column Break" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "frequency_of_depreciation", |      "fieldname": "frequency_of_depreciation", | ||||||
|    "fieldtype": "Int", |      "fieldtype": "Int", | ||||||
|    "label": "Frequency of Depreciation (Months)" |      "label": "Frequency of Depreciation (Months)" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "next_depreciation_date", |      "fieldname": "next_depreciation_date", | ||||||
|    "fieldtype": "Date", |      "fieldtype": "Date", | ||||||
|    "label": "Next Depreciation Date", |      "label": "Next Depreciation Date", | ||||||
|    "no_copy": 1 |      "no_copy": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "depends_on": "calculate_depreciation", |      "depends_on": "calculate_depreciation", | ||||||
|    "fieldname": "section_break_14", |      "fieldname": "section_break_14", | ||||||
|    "fieldtype": "Section Break", |      "fieldtype": "Section Break", | ||||||
|    "label": "Depreciation Schedule" |      "label": "Depreciation Schedule" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "schedules", |      "fieldname": "schedules", | ||||||
|    "fieldtype": "Table", |      "fieldtype": "Table", | ||||||
|    "label": "Depreciation Schedules", |      "label": "Depreciation Schedules", | ||||||
|    "no_copy": 1, |      "no_copy": 1, | ||||||
|    "options": "Depreciation Schedule" |      "options": "Depreciation Schedule" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "collapsible": 1, |      "collapsible": 1, | ||||||
|    "fieldname": "insurance_details", |      "fieldname": "insurance_details", | ||||||
|    "fieldtype": "Section Break", |      "fieldtype": "Section Break", | ||||||
|    "label": "Insurance details" |      "label": "Insurance details" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "policy_number", |      "fieldname": "policy_number", | ||||||
|    "fieldtype": "Data", |      "fieldtype": "Data", | ||||||
|    "label": "Policy number" |      "label": "Policy number" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "insurer", |      "fieldname": "insurer", | ||||||
|    "fieldtype": "Data", |      "fieldtype": "Data", | ||||||
|    "label": "Insurer" |      "label": "Insurer" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "insured_value", |      "fieldname": "insured_value", | ||||||
|    "fieldtype": "Data", |      "fieldtype": "Data", | ||||||
|    "label": "Insured value" |      "label": "Insured value" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "column_break_48", |      "fieldname": "column_break_48", | ||||||
|    "fieldtype": "Column Break" |      "fieldtype": "Column Break" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "insurance_start_date", |      "fieldname": "insurance_start_date", | ||||||
|    "fieldtype": "Date", |      "fieldtype": "Date", | ||||||
|    "label": "Insurance Start Date" |      "label": "Insurance Start Date" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "insurance_end_date", |      "fieldname": "insurance_end_date", | ||||||
|    "fieldtype": "Date", |      "fieldtype": "Date", | ||||||
|    "label": "Insurance End Date" |      "label": "Insurance End Date" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "comprehensive_insurance", |      "fieldname": "comprehensive_insurance", | ||||||
|    "fieldtype": "Data", |      "fieldtype": "Data", | ||||||
|    "label": "Comprehensive Insurance" |      "label": "Comprehensive Insurance" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "section_break_31", |      "fieldname": "section_break_31", | ||||||
|    "fieldtype": "Section Break", |      "fieldtype": "Section Break", | ||||||
|    "label": "Maintenance" |      "label": "Maintenance" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "allow_on_submit": 1, |      "allow_on_submit": 1, | ||||||
|    "default": "0", |      "default": "0", | ||||||
|    "description": "Check if Asset requires Preventive Maintenance or Calibration", |      "description": "Check if Asset requires Preventive Maintenance or Calibration", | ||||||
|    "fieldname": "maintenance_required", |      "fieldname": "maintenance_required", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Maintenance Required" |      "label": "Maintenance Required" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "collapsible": 1, |      "collapsible": 1, | ||||||
|    "fieldname": "other_details", |      "fieldname": "other_details", | ||||||
|    "fieldtype": "Section Break", |      "fieldtype": "Section Break", | ||||||
|    "label": "Other Details" |      "label": "Other Details" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "allow_on_submit": 1, |      "allow_on_submit": 1, | ||||||
|    "default": "Draft", |      "default": "Draft", | ||||||
|    "fieldname": "status", |      "fieldname": "status", | ||||||
|    "fieldtype": "Select", |      "fieldtype": "Select", | ||||||
|    "in_list_view": 1, |      "in_list_view": 1, | ||||||
|    "in_standard_filter": 1, |      "in_standard_filter": 1, | ||||||
|    "label": "Status", |      "label": "Status", | ||||||
|    "no_copy": 1, |      "no_copy": 1, | ||||||
|    "options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped\nIn Maintenance\nOut of Order\nIssue\nReceipt", |      "options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped\nIn Maintenance\nOut of Order\nIssue\nReceipt", | ||||||
|    "read_only": 1 |      "read_only": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "default": "0", |      "default": "0", | ||||||
|    "fieldname": "booked_fixed_asset", |      "fieldname": "booked_fixed_asset", | ||||||
|    "fieldtype": "Check", |      "fieldtype": "Check", | ||||||
|    "label": "Booked Fixed Asset", |      "label": "Booked Fixed Asset", | ||||||
|    "no_copy": 1, |      "no_copy": 1, | ||||||
|    "read_only": 1 |      "read_only": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "column_break_51", |      "fieldname": "column_break_51", | ||||||
|    "fieldtype": "Column Break" |      "fieldtype": "Column Break" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "purchase_receipt", |      "fieldname": "purchase_receipt", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "label": "Purchase Receipt", |      "label": "Purchase Receipt", | ||||||
|    "no_copy": 1, |      "no_copy": 1, | ||||||
|    "options": "Purchase Receipt", |      "options": "Purchase Receipt", | ||||||
|    "print_hide": 1, |      "print_hide": 1, | ||||||
|    "read_only": 1 |      "read_only": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "purchase_receipt_amount", |      "fieldname": "purchase_receipt_amount", | ||||||
|    "fieldtype": "Currency", |      "fieldtype": "Currency", | ||||||
|    "hidden": 1, |      "hidden": 1, | ||||||
|    "label": "Purchase Receipt Amount", |      "label": "Purchase Receipt Amount", | ||||||
|    "no_copy": 1, |      "no_copy": 1, | ||||||
|    "print_hide": 1, |      "print_hide": 1, | ||||||
|    "read_only": 1 |      "read_only": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "purchase_invoice", |      "fieldname": "purchase_invoice", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "label": "Purchase Invoice", |      "label": "Purchase Invoice", | ||||||
|    "no_copy": 1, |      "no_copy": 1, | ||||||
|    "options": "Purchase Invoice", |      "options": "Purchase Invoice", | ||||||
|    "read_only": 1 |      "read_only": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fetch_from": "company.default_finance_book", |      "fetch_from": "company.default_finance_book", | ||||||
|    "fieldname": "default_finance_book", |      "fieldname": "default_finance_book", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "hidden": 1, |      "hidden": 1, | ||||||
|    "label": "Default Finance Book", |      "label": "Default Finance Book", | ||||||
|    "options": "Finance Book", |      "options": "Finance Book", | ||||||
|    "read_only": 1 |      "read_only": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "amended_from", |      "fieldname": "amended_from", | ||||||
|    "fieldtype": "Link", |      "fieldtype": "Link", | ||||||
|    "label": "Amended From", |      "label": "Amended From", | ||||||
|    "no_copy": 1, |      "no_copy": 1, | ||||||
|    "options": "Asset", |      "options": "Asset", | ||||||
|    "print_hide": 1, |      "print_hide": 1, | ||||||
|    "read_only": 1 |      "read_only": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "collapsible": 1, |      "collapsible": 1, | ||||||
|    "fieldname": "accounting_dimensions_section", |      "fieldname": "accounting_dimensions_section", | ||||||
|    "fieldtype": "Section Break", |      "fieldtype": "Section Break", | ||||||
|    "label": "Accounting Dimensions" |      "label": "Accounting Dimensions" | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "fieldname": "dimension_col_break", |      "fieldname": "dimension_col_break", | ||||||
|    "fieldtype": "Column Break" |      "fieldtype": "Column Break" | ||||||
|   } |     } | ||||||
|  ], |    ], | ||||||
|  "idx": 72, |    "idx": 72, | ||||||
|  "image_field": "image", |    "image_field": "image", | ||||||
|  "is_submittable": 1, |    "is_submittable": 1, | ||||||
|  "modified": "2019-05-25 22:26:19.786201", |    "modified": "2019-05-25 22:26:19.786201", | ||||||
|  "modified_by": "Administrator", |    "modified_by": "Administrator", | ||||||
|  "module": "Assets", |    "module": "Assets", | ||||||
|  "name": "Asset", |    "name": "Asset", | ||||||
|  "owner": "Administrator", |    "owner": "Administrator", | ||||||
|  "permissions": [ |    "permissions": [ | ||||||
|   { |     { | ||||||
|    "amend": 1, |      "amend": 1, | ||||||
|    "cancel": 1, |      "cancel": 1, | ||||||
|    "create": 1, |      "create": 1, | ||||||
|    "delete": 1, |      "delete": 1, | ||||||
|    "email": 1, |      "email": 1, | ||||||
|    "export": 1, |      "export": 1, | ||||||
|    "import": 1, |      "import": 1, | ||||||
|    "print": 1, |      "print": 1, | ||||||
|    "read": 1, |      "read": 1, | ||||||
|    "report": 1, |      "report": 1, | ||||||
|    "role": "Accounts User", |      "role": "Accounts User", | ||||||
|    "share": 1, |      "share": 1, | ||||||
|    "submit": 1, |      "submit": 1, | ||||||
|    "write": 1 |      "write": 1 | ||||||
|   }, |     }, | ||||||
|   { |     { | ||||||
|    "cancel": 1, |      "cancel": 1, | ||||||
|    "create": 1, |      "create": 1, | ||||||
|    "delete": 1, |      "delete": 1, | ||||||
|    "email": 1, |      "email": 1, | ||||||
|    "export": 1, |      "export": 1, | ||||||
|    "print": 1, |      "print": 1, | ||||||
|    "read": 1, |      "read": 1, | ||||||
|    "report": 1, |      "report": 1, | ||||||
|    "role": "Quality Manager", |      "role": "Quality Manager", | ||||||
|    "share": 1, |      "share": 1, | ||||||
|    "submit": 1, |      "submit": 1, | ||||||
|    "write": 1 |      "write": 1 | ||||||
|   } |     } | ||||||
|  ], |    ], | ||||||
|  "show_name_in_global_search": 1, |    "show_name_in_global_search": 1, | ||||||
|  "sort_field": "modified", |    "sort_field": "modified", | ||||||
|  "sort_order": "DESC", |    "sort_order": "DESC", | ||||||
|  "title_field": "asset_name" |    "title_field": "asset_name" | ||||||
| } |   } | ||||||
| @ -510,4 +510,3 @@ def update_status(status, name): | |||||||
| def make_inter_company_sales_order(source_name, target_doc=None): | def make_inter_company_sales_order(source_name, target_doc=None): | ||||||
| 	from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction | 	from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction | ||||||
| 	return make_inter_company_transaction("Purchase Order", source_name, target_doc) | 	return make_inter_company_transaction("Purchase Order", source_name, target_doc) | ||||||
| 
 |  | ||||||
|  | |||||||
| @ -1,31 +1,31 @@ | |||||||
| { | { | ||||||
|  "add_total_row": 1, |     "add_total_row": 1, | ||||||
|  "creation": "2013-05-13 16:10:02", |     "creation": "2013-05-13 16:10:02", | ||||||
|  "disable_prepared_report": 0, |     "disable_prepared_report": 0, | ||||||
|  "disabled": 0, |     "disabled": 0, | ||||||
|  "docstatus": 0, |     "docstatus": 0, | ||||||
|  "doctype": "Report", |     "doctype": "Report", | ||||||
|  "idx": 3, |     "idx": 3, | ||||||
|  "is_standard": "Yes", |     "is_standard": "Yes", | ||||||
|  "modified": "2019-04-18 19:02:03.099422", |     "modified": "2019-04-18 19:02:03.099422", | ||||||
|  "modified_by": "Administrator", |     "modified_by": "Administrator", | ||||||
|  "module": "Buying", |     "module": "Buying", | ||||||
|  "name": "Requested Items To Be Ordered", |     "name": "Requested Items To Be Ordered", | ||||||
|  "owner": "Administrator", |     "owner": "Administrator", | ||||||
|  "prepared_report": 0, |     "prepared_report": 0, | ||||||
|  "query": "select \n    mr.name as \"Material Request:Link/Material Request:120\",\n\tmr.transaction_date as \"Date:Date:100\",\n\tmr_item.item_code as \"Item Code:Link/Item:120\",\n\tsum(ifnull(mr_item.stock_qty, 0)) as \"Qty:Float:100\",\n\tifnull(mr_item.stock_uom, '') as \"UOM:Link/UOM:100\",\n\tsum(ifnull(mr_item.ordered_qty, 0)) as \"Ordered Qty:Float:100\", \n\t(sum(mr_item.stock_qty) - sum(ifnull(mr_item.ordered_qty, 0))) as \"Qty to Order:Float:100\",\n\tmr_item.item_name as \"Item Name::150\",\n\tmr_item.description as \"Description::200\",\n\tmr.company as \"Company:Link/Company:\"\nfrom\n\t`tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n\tmr_item.parent = mr.name\n\tand mr.material_request_type = \"Purchase\"\n\tand mr.docstatus = 1\n\tand mr.status != \"Stopped\"\ngroup by mr.name, mr_item.item_code\nhaving\n\tsum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.stock_qty, 0))\norder by mr.transaction_date asc", |     "query": "select \n    mr.name as \"Material Request:Link/Material Request:120\",\n\tmr.transaction_date as \"Date:Date:100\",\n\tmr_item.item_code as \"Item Code:Link/Item:120\",\n\tsum(ifnull(mr_item.stock_qty, 0)) as \"Qty:Float:100\",\n\tifnull(mr_item.stock_uom, '') as \"UOM:Link/UOM:100\",\n\tsum(ifnull(mr_item.ordered_qty, 0)) as \"Ordered Qty:Float:100\", \n\t(sum(mr_item.stock_qty) - sum(ifnull(mr_item.ordered_qty, 0))) as \"Qty to Order:Float:100\",\n\tmr_item.item_name as \"Item Name::150\",\n\tmr_item.description as \"Description::200\",\n\tmr.company as \"Company:Link/Company:\"\nfrom\n\t`tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n\tmr_item.parent = mr.name\n\tand mr.material_request_type = \"Purchase\"\n\tand mr.docstatus = 1\n\tand mr.status != \"Stopped\"\ngroup by mr.name, mr_item.item_code\nhaving\n\tsum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.stock_qty, 0))\norder by mr.transaction_date asc", | ||||||
|  "ref_doctype": "Purchase Order", |     "ref_doctype": "Purchase Order", | ||||||
|  "report_name": "Requested Items To Be Ordered", |     "report_name": "Requested Items To Be Ordered", | ||||||
|  "report_type": "Query Report", |     "report_type": "Query Report", | ||||||
|  "roles": [ |     "roles": [ | ||||||
|   { |      { | ||||||
|    "role": "Stock User" |       "role": "Stock User" | ||||||
|   }, |      }, | ||||||
|   { |      { | ||||||
|    "role": "Purchase Manager" |       "role": "Purchase Manager" | ||||||
|   }, |      }, | ||||||
|   { |      { | ||||||
|    "role": "Purchase User" |       "role": "Purchase User" | ||||||
|   } |      } | ||||||
|  ] |     ] | ||||||
| } |    } | ||||||
| @ -167,7 +167,7 @@ def get_data(): | |||||||
| 					"type": "doctype", | 					"type": "doctype", | ||||||
| 					"name": "Opening Invoice Creation Tool", | 					"name": "Opening Invoice Creation Tool", | ||||||
| 					"description": _("Create Opening Sales and Purchase Invoices") | 					"description": _("Create Opening Sales and Purchase Invoices") | ||||||
| 				}, | 				} | ||||||
| 			] | 			] | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| @ -182,7 +182,7 @@ def get_data(): | |||||||
| 					"type": "doctype", | 					"type": "doctype", | ||||||
| 					"name": "Journal Entry", | 					"name": "Journal Entry", | ||||||
| 					"description": _("Accounting journal entries.") | 					"description": _("Accounting journal entries.") | ||||||
| 				}, | 				} | ||||||
| 			] | 			] | ||||||
| 		}, | 		}, | ||||||
| 		{ | 		{ | ||||||
| @ -246,6 +246,12 @@ def get_data(): | |||||||
| 					"label": _("Bank"), | 					"label": _("Bank"), | ||||||
| 					"name": "Bank", | 					"name": "Bank", | ||||||
| 				}, | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"type": "page", | ||||||
|  | 					"label": _("Reconcile payments and bank transactions"), | ||||||
|  | 					"name": "bank-reconciliation", | ||||||
|  | 					"description": _("Link bank transactions with payments.") | ||||||
|  | 				}, | ||||||
| 				{ | 				{ | ||||||
| 					"type": "doctype", | 					"type": "doctype", | ||||||
| 					"label": _("Bank Account"), | 					"label": _("Bank Account"), | ||||||
|  | |||||||
| @ -35,6 +35,11 @@ def get_data(): | |||||||
| 					"type": "doctype", | 					"type": "doctype", | ||||||
| 					"name": "Amazon MWS Settings", | 					"name": "Amazon MWS Settings", | ||||||
| 					"description": _("Connect Amazon with ERPNext"), | 					"description": _("Connect Amazon with ERPNext"), | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"type": "doctype", | ||||||
|  | 					"name": "Plaid Settings", | ||||||
|  | 					"description": _("Connect your bank accounts to ERPNext"), | ||||||
| 				} | 				} | ||||||
| 			] | 			] | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -304,6 +304,12 @@ def get_data(): | |||||||
| 					"name": "Customers Without Any Sales Transactions", | 					"name": "Customers Without Any Sales Transactions", | ||||||
| 					"doctype": "Customer" | 					"doctype": "Customer" | ||||||
| 				}, | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"type": "report", | ||||||
|  | 					"is_query_report": True, | ||||||
|  | 					"name": "Sales Partners Commission", | ||||||
|  | 					"doctype": "Customer" | ||||||
|  | 				}, | ||||||
| 				{ | 				{ | ||||||
| 					"type": "report", | 					"type": "report", | ||||||
| 					"is_query_report": True, | 					"is_query_report": True, | ||||||
| @ -312,6 +318,23 @@ def get_data(): | |||||||
| 				} | 				} | ||||||
| 			] | 			] | ||||||
| 		}, | 		}, | ||||||
|  | 		{ | ||||||
|  | 			"label": _("SMS"), | ||||||
|  | 			"icon": "fa fa-wrench", | ||||||
|  | 			"items": [ | ||||||
|  | 				{ | ||||||
|  | 					"type": "doctype", | ||||||
|  | 					"name": "SMS Center", | ||||||
|  | 					"description":_("Send mass SMS to your contacts"), | ||||||
|  | 				}, | ||||||
|  | 				{ | ||||||
|  | 					"type": "doctype", | ||||||
|  | 					"name": "SMS Log", | ||||||
|  | 					"description":_("Logs for maintaining sms delivery status"), | ||||||
|  | 				}, | ||||||
|  | 
 | ||||||
|  | 			] | ||||||
|  | 		}, | ||||||
| 		{ | 		{ | ||||||
| 			"label": _("Help"), | 			"label": _("Help"), | ||||||
| 			"items": [ | 			"items": [ | ||||||
|  | |||||||
| @ -32,8 +32,8 @@ class AccountsController(TransactionBase): | |||||||
| 		return self.__company_currency | 		return self.__company_currency | ||||||
| 
 | 
 | ||||||
| 	def onload(self): | 	def onload(self): | ||||||
| 		self.get("__onload").make_payment_via_journal_entry \ | 		self.set_onload("make_payment_via_journal_entry", | ||||||
| 			= frappe.db.get_single_value('Accounts Settings', 'make_payment_via_journal_entry') | 			frappe.db.get_single_value('Accounts Settings', 'make_payment_via_journal_entry')) | ||||||
| 
 | 
 | ||||||
| 		if self.is_new(): | 		if self.is_new(): | ||||||
| 			relevant_docs = ("Quotation", "Purchase Order", "Sales Order", | 			relevant_docs = ("Quotation", "Purchase Order", "Sales Order", | ||||||
| @ -242,6 +242,10 @@ class AccountsController(TransactionBase): | |||||||
| 				parent_dict.update({"document_type": document_type}) | 				parent_dict.update({"document_type": document_type}) | ||||||
| 
 | 
 | ||||||
| 			self.set('pricing_rules', []) | 			self.set('pricing_rules', []) | ||||||
|  | 			# party_name field used for customer in quotation | ||||||
|  | 			if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"): | ||||||
|  | 				parent_dict.update({"customer": parent_dict.get("party_name")}) | ||||||
|  | 
 | ||||||
| 			for item in self.get("items"): | 			for item in self.get("items"): | ||||||
| 				if item.get("item_code"): | 				if item.get("item_code"): | ||||||
| 					args = parent_dict.copy() | 					args = parent_dict.copy() | ||||||
| @ -1006,11 +1010,11 @@ def get_advance_journal_entries(party_type, party, party_account, amount_field, | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def get_advance_payment_entries(party_type, party, party_account, order_doctype, | def get_advance_payment_entries(party_type, party, party_account, order_doctype, | ||||||
| 		order_list=None, include_unallocated=True, against_all_orders=False, limit=1000): | 		order_list=None, include_unallocated=True, against_all_orders=False, limit=None): | ||||||
| 	party_account_field = "paid_from" if party_type == "Customer" else "paid_to" | 	party_account_field = "paid_from" if party_type == "Customer" else "paid_to" | ||||||
| 	payment_type = "Receive" if party_type == "Customer" else "Pay" | 	payment_type = "Receive" if party_type == "Customer" else "Pay" | ||||||
| 	payment_entries_against_order, unallocated_payment_entries = [], [] | 	payment_entries_against_order, unallocated_payment_entries = [], [] | ||||||
| 	limit_cond = "limit %s" % (limit or 1000) | 	limit_cond = "limit %s" % limit if limit else "" | ||||||
| 
 | 
 | ||||||
| 	if order_list or against_all_orders: | 	if order_list or against_all_orders: | ||||||
| 		if order_list: | 		if order_list: | ||||||
|  | |||||||
| @ -176,7 +176,7 @@ def enqueue_multiple_variant_creation(item, args): | |||||||
| 	for key in variants: | 	for key in variants: | ||||||
| 		total_variants *= len(variants[key]) | 		total_variants *= len(variants[key]) | ||||||
| 	if total_variants >= 600: | 	if total_variants >= 600: | ||||||
| 		frappe.msgprint("Please do not create more than 500 items at a time", raise_exception=1) | 		frappe.throw(_("Please do not create more than 500 items at a time")) | ||||||
| 		return | 		return | ||||||
| 	if total_variants < 10: | 	if total_variants < 10: | ||||||
| 		return create_multiple_variants(item, args) | 		return create_multiple_variants(item, args) | ||||||
| @ -282,7 +282,7 @@ def copy_attributes_to_variant(item, variant): | |||||||
| 
 | 
 | ||||||
| 	if 'description' not in allow_fields: | 	if 'description' not in allow_fields: | ||||||
| 		if not variant.description: | 		if not variant.description: | ||||||
| 			variant.description = "" | 				variant.description = "" | ||||||
| 
 | 
 | ||||||
| 		if item.variant_based_on=='Item Attribute': | 		if item.variant_based_on=='Item Attribute': | ||||||
| 			if variant.attributes: | 			if variant.attributes: | ||||||
|  | |||||||
| @ -205,11 +205,14 @@ def get_already_returned_items(doc): | |||||||
| 
 | 
 | ||||||
| def make_return_doc(doctype, source_name, target_doc=None): | def make_return_doc(doctype, source_name, target_doc=None): | ||||||
| 	from frappe.model.mapper import get_mapped_doc | 	from frappe.model.mapper import get_mapped_doc | ||||||
|  | 	company = frappe.db.get_value("Delivery Note", source_name, "company") | ||||||
|  | 	default_warehouse_for_sales_return = frappe.db.get_value("Company", company, "default_warehouse_for_sales_return") | ||||||
| 	def set_missing_values(source, target): | 	def set_missing_values(source, target): | ||||||
| 		doc = frappe.get_doc(target) | 		doc = frappe.get_doc(target) | ||||||
| 		doc.is_return = 1 | 		doc.is_return = 1 | ||||||
| 		doc.return_against = source.name | 		doc.return_against = source.name | ||||||
| 		doc.ignore_pricing_rule = 1 | 		doc.ignore_pricing_rule = 1 | ||||||
|  | 		doc.set_warehouse = "" | ||||||
| 		if doctype == "Sales Invoice": | 		if doctype == "Sales Invoice": | ||||||
| 			doc.is_pos = source.is_pos | 			doc.is_pos = source.is_pos | ||||||
| 
 | 
 | ||||||
| @ -253,7 +256,6 @@ def make_return_doc(doctype, source_name, target_doc=None): | |||||||
| 
 | 
 | ||||||
| 	def update_item(source_doc, target_doc, source_parent): | 	def update_item(source_doc, target_doc, source_parent): | ||||||
| 		target_doc.qty = -1* source_doc.qty | 		target_doc.qty = -1* source_doc.qty | ||||||
| 		default_return_warehouse = frappe.db.get_single_value("Stock Settings", "default_return_warehouse") |  | ||||||
| 		if doctype == "Purchase Receipt": | 		if doctype == "Purchase Receipt": | ||||||
| 			target_doc.received_qty = -1* source_doc.received_qty | 			target_doc.received_qty = -1* source_doc.received_qty | ||||||
| 			target_doc.rejected_qty = -1* source_doc.rejected_qty | 			target_doc.rejected_qty = -1* source_doc.rejected_qty | ||||||
| @ -278,13 +280,16 @@ def make_return_doc(doctype, source_name, target_doc=None): | |||||||
| 			target_doc.so_detail = source_doc.so_detail | 			target_doc.so_detail = source_doc.so_detail | ||||||
| 			target_doc.si_detail = source_doc.si_detail | 			target_doc.si_detail = source_doc.si_detail | ||||||
| 			target_doc.expense_account = source_doc.expense_account | 			target_doc.expense_account = source_doc.expense_account | ||||||
| 			target_doc.warehouse = default_return_warehouse if default_return_warehouse else source_doc.warehouse | 			if default_warehouse_for_sales_return: | ||||||
|  | 				target_doc.warehouse = default_warehouse_for_sales_return | ||||||
| 		elif doctype == "Sales Invoice": | 		elif doctype == "Sales Invoice": | ||||||
| 			target_doc.sales_order = source_doc.sales_order | 			target_doc.sales_order = source_doc.sales_order | ||||||
| 			target_doc.delivery_note = source_doc.delivery_note | 			target_doc.delivery_note = source_doc.delivery_note | ||||||
| 			target_doc.so_detail = source_doc.so_detail | 			target_doc.so_detail = source_doc.so_detail | ||||||
| 			target_doc.dn_detail = source_doc.dn_detail | 			target_doc.dn_detail = source_doc.dn_detail | ||||||
| 			target_doc.expense_account = source_doc.expense_account | 			target_doc.expense_account = source_doc.expense_account | ||||||
|  | 			if default_warehouse_for_sales_return: | ||||||
|  | 				target_doc.warehouse = default_warehouse_for_sales_return | ||||||
| 
 | 
 | ||||||
| 	def update_terms(source_doc, target_doc, source_parent): | 	def update_terms(source_doc, target_doc, source_parent): | ||||||
| 		target_doc.payment_amount = -source_doc.payment_amount | 		target_doc.payment_amount = -source_doc.payment_amount | ||||||
|  | |||||||
| @ -55,14 +55,28 @@ class SellingController(StockController): | |||||||
| 		self.set_price_list_and_item_details(for_validate=for_validate) | 		self.set_price_list_and_item_details(for_validate=for_validate) | ||||||
| 
 | 
 | ||||||
| 	def set_missing_lead_customer_details(self): | 	def set_missing_lead_customer_details(self): | ||||||
|  | 		customer, lead = None, None | ||||||
| 		if getattr(self, "customer", None): | 		if getattr(self, "customer", None): | ||||||
|  | 			customer = self.customer | ||||||
|  | 		elif self.doctype == "Opportunity" and self.party_name: | ||||||
|  | 			if self.opportunity_from == "Customer": | ||||||
|  | 				customer = self.party_name | ||||||
|  | 			else: | ||||||
|  | 				lead = self.party_name | ||||||
|  | 		elif self.doctype == "Quotation" and self.party_name: | ||||||
|  | 			if self.quotation_to == "Customer": | ||||||
|  | 				customer = self.party_name | ||||||
|  | 			else: | ||||||
|  | 				lead = self.party_name | ||||||
|  | 
 | ||||||
|  | 		if customer: | ||||||
| 			from erpnext.accounts.party import _get_party_details | 			from erpnext.accounts.party import _get_party_details | ||||||
| 			fetch_payment_terms_template = False | 			fetch_payment_terms_template = False | ||||||
| 			if (self.get("__islocal") or | 			if (self.get("__islocal") or | ||||||
| 				self.company != frappe.db.get_value(self.doctype, self.name, 'company')): | 				self.company != frappe.db.get_value(self.doctype, self.name, 'company')): | ||||||
| 				fetch_payment_terms_template = True | 				fetch_payment_terms_template = True | ||||||
| 
 | 
 | ||||||
| 			party_details = _get_party_details(self.customer, | 			party_details = _get_party_details(customer, | ||||||
| 				ignore_permissions=self.flags.ignore_permissions, | 				ignore_permissions=self.flags.ignore_permissions, | ||||||
| 				doctype=self.doctype, company=self.company, | 				doctype=self.doctype, company=self.company, | ||||||
| 				fetch_payment_terms_template=fetch_payment_terms_template, | 				fetch_payment_terms_template=fetch_payment_terms_template, | ||||||
| @ -71,10 +85,9 @@ class SellingController(StockController): | |||||||
| 				party_details.pop("sales_team") | 				party_details.pop("sales_team") | ||||||
| 			self.update_if_missing(party_details) | 			self.update_if_missing(party_details) | ||||||
| 
 | 
 | ||||||
| 		elif getattr(self, "lead", None): | 		elif lead: | ||||||
| 			from erpnext.crm.doctype.lead.lead import get_lead_details | 			from erpnext.crm.doctype.lead.lead import get_lead_details | ||||||
| 			self.update_if_missing(get_lead_details( | 			self.update_if_missing(get_lead_details(lead, | ||||||
| 				self.lead, |  | ||||||
| 				posting_date=self.get('transaction_date') or self.get('posting_date'), | 				posting_date=self.get('transaction_date') or self.get('posting_date'), | ||||||
| 				company=self.company)) | 				company=self.company)) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -46,9 +46,9 @@ status_map = { | |||||||
| 	"Sales Invoice": [ | 	"Sales Invoice": [ | ||||||
| 		["Draft", None], | 		["Draft", None], | ||||||
| 		["Submitted", "eval:self.docstatus==1"], | 		["Submitted", "eval:self.docstatus==1"], | ||||||
|  | 		["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"], | ||||||
| 		["Return", "eval:self.is_return==1 and self.docstatus==1"], | 		["Return", "eval:self.is_return==1 and self.docstatus==1"], | ||||||
| 		["Paid", "eval:self.outstanding_amount<=0 and self.docstatus==1 and self.is_return==0"], | 		["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1"], | ||||||
| 		["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1 and self.is_return==0 and get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"], |  | ||||||
| 		["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], | 		["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], | ||||||
| 		["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], | 		["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], | ||||||
| 		["Cancelled", "eval:self.docstatus==2"], | 		["Cancelled", "eval:self.docstatus==2"], | ||||||
| @ -56,9 +56,9 @@ status_map = { | |||||||
| 	"Purchase Invoice": [ | 	"Purchase Invoice": [ | ||||||
| 		["Draft", None], | 		["Draft", None], | ||||||
| 		["Submitted", "eval:self.docstatus==1"], | 		["Submitted", "eval:self.docstatus==1"], | ||||||
|  | 		["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"], | ||||||
| 		["Return", "eval:self.is_return==1 and self.docstatus==1"], | 		["Return", "eval:self.is_return==1 and self.docstatus==1"], | ||||||
| 		["Paid", "eval:self.outstanding_amount<=0 and self.docstatus==1 and self.is_return==0"], | 		["Debit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1"], | ||||||
| 		["Debit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"], |  | ||||||
| 		["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], | 		["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"], | ||||||
| 		["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], | 		["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"], | ||||||
| 		["Cancelled", "eval:self.docstatus==2"], | 		["Cancelled", "eval:self.docstatus==2"], | ||||||
| @ -99,6 +99,10 @@ status_map = { | |||||||
| 		["Issued", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'"], | 		["Issued", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'"], | ||||||
| 		["Received", "eval:self.status != 'Stopped' and self.per_received == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"], | 		["Received", "eval:self.status != 'Stopped' and self.per_received == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"], | ||||||
| 		["Partially Received", "eval:self.status != 'Stopped' and self.per_received > 0 and self.per_received < 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"] | 		["Partially Received", "eval:self.status != 'Stopped' and self.per_received > 0 and self.per_received < 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"] | ||||||
|  | 	], | ||||||
|  | 	"Bank Transaction": [ | ||||||
|  | 		["Unreconciled", "eval:self.docstatus == 1 and self.unallocated_amount>0"], | ||||||
|  | 		["Reconciled", "eval:self.docstatus == 1 and self.unallocated_amount<=0"] | ||||||
| 	] | 	] | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -128,7 +128,7 @@ class StockController(AccountsController): | |||||||
| 			reconciliation_purpose = frappe.db.get_value(self.doctype, self.name, "purpose") | 			reconciliation_purpose = frappe.db.get_value(self.doctype, self.name, "purpose") | ||||||
| 			is_opening = "Yes" if reconciliation_purpose == "Opening Stock" else "No" | 			is_opening = "Yes" if reconciliation_purpose == "Opening Stock" else "No" | ||||||
| 			details = [] | 			details = [] | ||||||
| 			for voucher_detail_no, sle in sle_map.items(): | 			for voucher_detail_no in sle_map: | ||||||
| 				details.append(frappe._dict({ | 				details.append(frappe._dict({ | ||||||
| 					"name": voucher_detail_no, | 					"name": voucher_detail_no, | ||||||
| 					"expense_account": default_expense_account, | 					"expense_account": default_expense_account, | ||||||
| @ -362,10 +362,12 @@ class StockController(AccountsController): | |||||||
| 					frappe.throw(_("Row {0}: Quality Inspection rejected for item {1}") | 					frappe.throw(_("Row {0}: Quality Inspection rejected for item {1}") | ||||||
| 						.format(d.idx, d.item_code), QualityInspectionRejectedError) | 						.format(d.idx, d.item_code), QualityInspectionRejectedError) | ||||||
| 			elif qa_required : | 			elif qa_required : | ||||||
| 				frappe.msgprint(_("Quality Inspection required for Item {0}").format(d.item_code)) | 				action = frappe.get_doc('Stock Settings').action_if_quality_inspection_is_not_submitted | ||||||
| 				if self.docstatus==1: | 				if self.docstatus==1 and action == 'Stop': | ||||||
| 					raise QualityInspectionRequiredError | 					frappe.throw(_("Quality Inspection required for Item {0} to submit").format(frappe.bold(d.item_code)), | ||||||
| 
 | 						exc=QualityInspectionRequiredError) | ||||||
|  | 				else: | ||||||
|  | 					frappe.msgprint(_("Create Quality Inspection for Item {0}").format(frappe.bold(d.item_code))) | ||||||
| 
 | 
 | ||||||
| 	def update_blanket_order(self): | 	def update_blanket_order(self): | ||||||
| 		blanket_orders = list(set([d.blanket_order for d in self.items if d.blanket_order])) | 		blanket_orders = list(set([d.blanket_order for d in self.items if d.blanket_order])) | ||||||
|  | |||||||
| @ -616,7 +616,7 @@ def get_itemised_tax_breakup_data(doc): | |||||||
| 
 | 
 | ||||||
| 	return itemised_tax, itemised_taxable_amount | 	return itemised_tax, itemised_taxable_amount | ||||||
| 
 | 
 | ||||||
| def get_itemised_tax(taxes): | def get_itemised_tax(taxes, with_tax_account=False): | ||||||
| 	itemised_tax = {} | 	itemised_tax = {} | ||||||
| 	for tax in taxes: | 	for tax in taxes: | ||||||
| 		if getattr(tax, "category", None) and tax.category=="Valuation": | 		if getattr(tax, "category", None) and tax.category=="Valuation": | ||||||
| @ -641,6 +641,9 @@ def get_itemised_tax(taxes): | |||||||
| 					tax_amount = tax_amount | 					tax_amount = tax_amount | ||||||
| 				)) | 				)) | ||||||
| 
 | 
 | ||||||
|  | 				if with_tax_account: | ||||||
|  | 					itemised_tax[item_code][tax.description].tax_account = tax.account_head | ||||||
|  | 
 | ||||||
| 	return itemised_tax | 	return itemised_tax | ||||||
| 
 | 
 | ||||||
| def get_itemised_taxable_amount(items): | def get_itemised_taxable_amount(items): | ||||||
|  | |||||||
| @ -43,7 +43,7 @@ class TestMapper(unittest.TestCase): | |||||||
| 		qtn = frappe.get_doc({ | 		qtn = frappe.get_doc({ | ||||||
| 			"doctype": "Quotation", | 			"doctype": "Quotation", | ||||||
| 			"quotation_to": "Customer", | 			"quotation_to": "Customer", | ||||||
| 			"customer": customer, | 			"party_name": customer, | ||||||
| 			"order_type": "Sales", | 			"order_type": "Sales", | ||||||
| 			"transaction_date" : nowdate(), | 			"transaction_date" : nowdate(), | ||||||
| 			"valid_till" : add_months(nowdate(), 1) | 			"valid_till" : add_months(nowdate(), 1) | ||||||
|  | |||||||
| @ -6,6 +6,17 @@ cur_frm.email_field = "email_id"; | |||||||
| 
 | 
 | ||||||
| erpnext.LeadController = frappe.ui.form.Controller.extend({ | erpnext.LeadController = frappe.ui.form.Controller.extend({ | ||||||
| 	setup: function () { | 	setup: function () { | ||||||
|  | 		this.frm.make_methods = { | ||||||
|  | 			'Quotation': () => erpnext.utils.create_new_doc('Quotation', { | ||||||
|  | 				'quotation_to': this.frm.doc.doctype, | ||||||
|  | 				'party_name': this.frm.doc.name | ||||||
|  | 			}), | ||||||
|  | 			'Opportunity': () => erpnext.utils.create_new_doc('Opportunity', { | ||||||
|  | 				'opportunity_from': this.frm.doc.doctype, | ||||||
|  | 				'party_name': this.frm.doc.name | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		this.frm.fields_dict.customer.get_query = function (doc, cdt, cdn) { | 		this.frm.fields_dict.customer.get_query = function (doc, cdt, cdn) { | ||||||
| 			return { query: "erpnext.controllers.queries.customer_query" } | 			return { query: "erpnext.controllers.queries.customer_query" } | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -1430,7 +1430,7 @@ | |||||||
|  "issingle": 0, |  "issingle": 0, | ||||||
|  "istable": 0, |  "istable": 0, | ||||||
|  "max_attachments": 0, |  "max_attachments": 0, | ||||||
|  "modified": "2019-05-10 03:22:57.283628", |  "modified": "2019-06-18 03:22:57.283628", | ||||||
|  "modified_by": "Administrator", |  "modified_by": "Administrator", | ||||||
|  "module": "CRM", |  "module": "CRM", | ||||||
|  "name": "Lead", |  "name": "Lead", | ||||||
|  | |||||||
| @ -90,11 +90,11 @@ class Lead(SellingController): | |||||||
| 		return frappe.db.get_value("Customer", {"lead_name": self.name}) | 		return frappe.db.get_value("Customer", {"lead_name": self.name}) | ||||||
| 
 | 
 | ||||||
| 	def has_opportunity(self): | 	def has_opportunity(self): | ||||||
| 		return frappe.db.get_value("Opportunity", {"lead": self.name, "status": ["!=", "Lost"]}) | 		return frappe.db.get_value("Opportunity", {"party_name": self.name, "status": ["!=", "Lost"]}) | ||||||
| 
 | 
 | ||||||
| 	def has_quotation(self): | 	def has_quotation(self): | ||||||
| 		return frappe.db.get_value("Quotation", { | 		return frappe.db.get_value("Quotation", { | ||||||
| 			"lead": self.name, | 			"party_name": self.name, | ||||||
| 			"docstatus": 1, | 			"docstatus": 1, | ||||||
| 			"status": ["!=", "Lost"] | 			"status": ["!=", "Lost"] | ||||||
| 
 | 
 | ||||||
| @ -102,7 +102,7 @@ class Lead(SellingController): | |||||||
| 
 | 
 | ||||||
| 	def has_lost_quotation(self): | 	def has_lost_quotation(self): | ||||||
| 		return frappe.db.get_value("Quotation", { | 		return frappe.db.get_value("Quotation", { | ||||||
| 			"lead": self.name, | 			"party_name": self.name, | ||||||
| 			"docstatus": 1, | 			"docstatus": 1, | ||||||
| 			"status": "Lost" | 			"status": "Lost" | ||||||
| 		}) | 		}) | ||||||
| @ -150,8 +150,8 @@ def make_opportunity(source_name, target_doc=None): | |||||||
| 			"doctype": "Opportunity", | 			"doctype": "Opportunity", | ||||||
| 			"field_map": { | 			"field_map": { | ||||||
| 				"campaign_name": "campaign", | 				"campaign_name": "campaign", | ||||||
| 				"doctype": "enquiry_from", | 				"doctype": "opportunity_from", | ||||||
| 				"name": "lead", | 				"name": "party_name", | ||||||
| 				"lead_name": "contact_display", | 				"lead_name": "contact_display", | ||||||
| 				"company_name": "customer_name", | 				"company_name": "customer_name", | ||||||
| 				"email_id": "contact_email", | 				"email_id": "contact_email", | ||||||
| @ -167,7 +167,7 @@ def make_quotation(source_name, target_doc=None): | |||||||
| 		{"Lead": { | 		{"Lead": { | ||||||
| 			"doctype": "Quotation", | 			"doctype": "Quotation", | ||||||
| 			"field_map": { | 			"field_map": { | ||||||
| 				"name": "lead" | 				"name": "party_name" | ||||||
| 			} | 			} | ||||||
| 		}}, target_doc) | 		}}, target_doc) | ||||||
| 	target_doc.quotation_to = "Lead" | 	target_doc.quotation_to = "Lead" | ||||||
| @ -190,7 +190,7 @@ def get_lead_details(lead, posting_date=None, company=None): | |||||||
| 	out.update({ | 	out.update({ | ||||||
| 		"territory": lead.territory, | 		"territory": lead.territory, | ||||||
| 		"customer_name": lead.company_name or lead.lead_name, | 		"customer_name": lead.company_name or lead.lead_name, | ||||||
| 		"contact_display": lead.lead_name, | 		"contact_display": " ".join(filter(None, [lead.salutation, lead.lead_name])), | ||||||
| 		"contact_email": lead.email_id, | 		"contact_email": lead.email_id, | ||||||
| 		"contact_mobile": lead.mobile_no, | 		"contact_mobile": lead.mobile_no, | ||||||
| 		"contact_phone": lead.phone, | 		"contact_phone": lead.phone, | ||||||
|  | |||||||
| @ -4,6 +4,13 @@ from frappe import _ | |||||||
| def get_data(): | def get_data(): | ||||||
| 	return { | 	return { | ||||||
| 		'fieldname': 'lead', | 		'fieldname': 'lead', | ||||||
|  | 		'non_standard_fieldnames': { | ||||||
|  | 			'Quotation': 'party_name', | ||||||
|  | 			'Opportunity': 'party_name' | ||||||
|  | 		}, | ||||||
|  | 		'dynamic_links': { | ||||||
|  | 			'party_name': ['Lead', 'quotation_to'] | ||||||
|  | 		}, | ||||||
| 		'transactions': [ | 		'transactions': [ | ||||||
| 			{ | 			{ | ||||||
| 				'items': ['Opportunity', 'Quotation'] | 				'items': ['Opportunity', 'Quotation'] | ||||||
|  | |||||||
| @ -10,15 +10,34 @@ frappe.ui.form.on("Opportunity", { | |||||||
| 		frm.custom_make_buttons = { | 		frm.custom_make_buttons = { | ||||||
| 			'Quotation': 'Quotation', | 			'Quotation': 'Quotation', | ||||||
| 			'Supplier Quotation': 'Supplier Quotation' | 			'Supplier Quotation': 'Supplier Quotation' | ||||||
| 		} | 		}, | ||||||
| 	}, | 
 | ||||||
| 	customer: function(frm) { | 		frm.set_query("opportunity_from", function() { | ||||||
| 		frm.trigger('set_contact_link'); | 			return{ | ||||||
| 		erpnext.utils.get_party_details(frm); | 				"filters": { | ||||||
|  | 					"name": ["in", ["Customer", "Lead"]], | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		}); | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	lead: function(frm) { | 	onload_post_render: function(frm) { | ||||||
| 		frm.trigger('set_contact_link'); | 		frm.get_field("items").grid.set_multiple_add("item_code", "qty"); | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	party_name: function(frm) { | ||||||
|  | 		frm.toggle_display("contact_info", frm.doc.party_name); | ||||||
|  | 
 | ||||||
|  | 		if (frm.doc.opportunity_from == "Customer") { | ||||||
|  | 			frm.trigger('set_contact_link'); | ||||||
|  | 			erpnext.utils.get_party_details(frm); | ||||||
|  | 		} else if (frm.doc.opportunity_from == "Lead") { | ||||||
|  | 			erpnext.utils.map_current_doc({ | ||||||
|  | 				method: "erpnext.crm.doctype.lead.lead.make_opportunity", | ||||||
|  | 				source_name: frm.doc.party_name, | ||||||
|  | 				frm: frm | ||||||
|  | 			}); | ||||||
|  | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	onload_post_render: function(frm) { | 	onload_post_render: function(frm) { | ||||||
| @ -42,15 +61,14 @@ frappe.ui.form.on("Opportunity", { | |||||||
| 
 | 
 | ||||||
| 	contact_person: erpnext.utils.get_contact_details, | 	contact_person: erpnext.utils.get_contact_details, | ||||||
| 
 | 
 | ||||||
| 	enquiry_from: function(frm) { | 	opportunity_from: function(frm) { | ||||||
| 		frm.toggle_reqd("lead", frm.doc.enquiry_from==="Lead"); | 		frm.toggle_reqd("party_name", frm.doc.opportunity_from); | ||||||
| 		frm.toggle_reqd("customer", frm.doc.enquiry_from==="Customer"); | 		frm.trigger("set_dynamic_field_label"); | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	refresh: function(frm) { | 	refresh: function(frm) { | ||||||
| 		var doc = frm.doc; | 		var doc = frm.doc; | ||||||
| 		frm.events.enquiry_from(frm); | 		frm.events.opportunity_from(frm); | ||||||
| 		frm.trigger('set_contact_link'); |  | ||||||
| 		frm.trigger('toggle_mandatory'); | 		frm.trigger('toggle_mandatory'); | ||||||
| 		erpnext.toggle_naming_series(); | 		erpnext.toggle_naming_series(); | ||||||
| 
 | 
 | ||||||
| @ -88,10 +106,17 @@ frappe.ui.form.on("Opportunity", { | |||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	set_contact_link: function(frm) { | 	set_contact_link: function(frm) { | ||||||
| 		if(frm.doc.customer) { | 		if(frm.doc.opportunity_from == "Customer" && frm.doc.party_name) { | ||||||
| 			frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'} | 			frappe.dynamic_link = {doc: frm.doc, fieldname: 'party_name', doctype: 'Customer'} | ||||||
| 		} else if(frm.doc.lead) { | 		} else if(frm.doc.opportunity_from == "Lead" && frm.doc.party_name) { | ||||||
| 			frappe.dynamic_link = {doc: frm.doc, fieldname: 'lead', doctype: 'Lead'} | 			frappe.dynamic_link = {doc: frm.doc, fieldname: 'party_name', doctype: 'Lead'} | ||||||
|  | 		} | ||||||
|  | 	}, | ||||||
|  | 
 | ||||||
|  | 	set_dynamic_field_label: function(frm){ | ||||||
|  | 
 | ||||||
|  | 		if (frm.doc.opportunity_from) { | ||||||
|  | 			frm.set_df_property("party_name", "label", frm.doc.opportunity_from); | ||||||
| 		} | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| @ -110,10 +135,6 @@ frappe.ui.form.on("Opportunity", { | |||||||
| // TODO commonify this code
 | // TODO commonify this code
 | ||||||
| erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({ | erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({ | ||||||
| 	onload: function() { | 	onload: function() { | ||||||
| 		if(!this.frm.doc.enquiry_from && this.frm.doc.customer) |  | ||||||
| 			this.frm.doc.enquiry_from = "Customer"; |  | ||||||
| 		if(!this.frm.doc.enquiry_from && this.frm.doc.lead) |  | ||||||
| 			this.frm.doc.enquiry_from = "Lead"; |  | ||||||
| 
 | 
 | ||||||
| 		if(!this.frm.doc.status) { | 		if(!this.frm.doc.status) { | ||||||
| 			frm.set_value('status', 'Open'); | 			frm.set_value('status', 'Open'); | ||||||
| @ -144,12 +165,14 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({ | |||||||
| 			}; | 			}; | ||||||
| 		}); | 		}); | ||||||
| 
 | 
 | ||||||
| 		$.each([["lead", "lead"], | 		me.frm.set_query('contact_person', erpnext.queries['contact_query']) | ||||||
| 			["customer", "customer"], | 
 | ||||||
| 			["contact_person", "contact_query"]], | 		if (me.frm.doc.opportunity_from == "Lead") { | ||||||
| 			function(i, opts) { | 			me.frm.set_query('party_name', erpnext.queries['lead']); | ||||||
| 				me.frm.set_query(opts[0], erpnext.queries[opts[1]]); | 		} | ||||||
| 			}); | 		else if (me.frm.doc.opportunity_from == "Cuatomer") { | ||||||
|  | 			me.frm.set_query('party_name', erpnext.queries['customer']); | ||||||
|  | 		} | ||||||
| 	}, | 	}, | ||||||
| 
 | 
 | ||||||
| 	create_quotation: function() { | 	create_quotation: function() { | ||||||
| @ -162,11 +185,6 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({ | |||||||
| 
 | 
 | ||||||
| $.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm})); | $.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm})); | ||||||
| 
 | 
 | ||||||
| cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) { |  | ||||||
| 	if(doc.enquiry_from == 'Lead' && doc.lead) |  | ||||||
| 		cur_frm.cscript.lead(doc, cdt, cdn); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| cur_frm.cscript.item_code = function(doc, cdt, cdn) { | cur_frm.cscript.item_code = function(doc, cdt, cdn) { | ||||||
| 	var d = locals[cdt][cdn]; | 	var d = locals[cdt][cdn]; | ||||||
| 	if (d.item_code) { | 	if (d.item_code) { | ||||||
| @ -183,13 +201,4 @@ cur_frm.cscript.item_code = function(doc, cdt, cdn) { | |||||||
| 			} | 			} | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 |  | ||||||
| cur_frm.cscript.lead = function(doc, cdt, cdn) { |  | ||||||
| 	cur_frm.toggle_display("contact_info", doc.customer || doc.lead); |  | ||||||
| 	erpnext.utils.map_current_doc({ |  | ||||||
| 		method: "erpnext.crm.doctype.lead.lead.make_opportunity", |  | ||||||
| 		source_name: cur_frm.doc.lead, |  | ||||||
| 		frm: cur_frm |  | ||||||
| 	}); |  | ||||||
| } |  | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -16,8 +16,8 @@ sender_field = "contact_email" | |||||||
| 
 | 
 | ||||||
| class Opportunity(TransactionBase): | class Opportunity(TransactionBase): | ||||||
| 	def after_insert(self): | 	def after_insert(self): | ||||||
| 		if self.lead: | 		if self.opportunity_from == "Lead": | ||||||
| 			frappe.get_doc("Lead", self.lead).set_status(update=True) | 			frappe.get_doc("Lead", self.party_name).set_status(update=True) | ||||||
| 
 | 
 | ||||||
| 	def validate(self): | 	def validate(self): | ||||||
| 		self._prev = frappe._dict({ | 		self._prev = frappe._dict({ | ||||||
| @ -29,12 +29,8 @@ class Opportunity(TransactionBase): | |||||||
| 
 | 
 | ||||||
| 		self.make_new_lead_if_required() | 		self.make_new_lead_if_required() | ||||||
| 
 | 
 | ||||||
| 		if not self.enquiry_from: |  | ||||||
| 			frappe.throw(_("Opportunity From field is mandatory")) |  | ||||||
| 
 |  | ||||||
| 		self.validate_item_details() | 		self.validate_item_details() | ||||||
| 		self.validate_uom_is_integer("uom", "qty") | 		self.validate_uom_is_integer("uom", "qty") | ||||||
| 		self.validate_lead_cust() |  | ||||||
| 		self.validate_cust_name() | 		self.validate_cust_name() | ||||||
| 
 | 
 | ||||||
| 		if not self.title: | 		if not self.title: | ||||||
| @ -45,7 +41,7 @@ class Opportunity(TransactionBase): | |||||||
| 
 | 
 | ||||||
| 	def make_new_lead_if_required(self): | 	def make_new_lead_if_required(self): | ||||||
| 		"""Set lead against new opportunity""" | 		"""Set lead against new opportunity""" | ||||||
| 		if not (self.lead or self.customer) and self.contact_email: | 		if (not self.get("party_name")) and self.contact_email: | ||||||
| 			# check if customer is already created agains the self.contact_email | 			# check if customer is already created agains the self.contact_email | ||||||
| 			customer = frappe.db.sql("""select | 			customer = frappe.db.sql("""select | ||||||
| 				distinct `tabDynamic Link`.link_name as customer | 				distinct `tabDynamic Link`.link_name as customer | ||||||
| @ -61,8 +57,8 @@ class Opportunity(TransactionBase): | |||||||
| 					`tabDynamic Link`.link_doctype='Customer' | 					`tabDynamic Link`.link_doctype='Customer' | ||||||
| 			""".format(self.contact_email), as_dict=True) | 			""".format(self.contact_email), as_dict=True) | ||||||
| 			if customer and customer[0].customer: | 			if customer and customer[0].customer: | ||||||
| 				self.customer = customer[0].customer | 				self.party_name = customer[0].customer | ||||||
| 				self.enquiry_from = "Customer" | 				self.opportunity_from = "Customer" | ||||||
| 				return | 				return | ||||||
| 
 | 
 | ||||||
| 			lead_name = frappe.db.get_value("Lead", {"email_id": self.contact_email}) | 			lead_name = frappe.db.get_value("Lead", {"email_id": self.contact_email}) | ||||||
| @ -89,8 +85,8 @@ class Opportunity(TransactionBase): | |||||||
| 				lead.insert(ignore_permissions=True) | 				lead.insert(ignore_permissions=True) | ||||||
| 				lead_name = lead.name | 				lead_name = lead.name | ||||||
| 
 | 
 | ||||||
| 			self.enquiry_from = "Lead" | 			self.opportunity_from = "Lead" | ||||||
| 			self.lead = lead_name | 			self.party_name = lead_name | ||||||
| 
 | 
 | ||||||
| 	def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None): | 	def declare_enquiry_lost(self, lost_reasons_list, detailed_reason=None): | ||||||
| 		if not self.has_active_quotation(): | 		if not self.has_active_quotation(): | ||||||
| @ -145,10 +141,10 @@ class Opportunity(TransactionBase): | |||||||
| 			return True | 			return True | ||||||
| 
 | 
 | ||||||
| 	def validate_cust_name(self): | 	def validate_cust_name(self): | ||||||
| 		if self.customer: | 		if self.party_name and self.opportunity_from == 'Customer': | ||||||
| 			self.customer_name = frappe.db.get_value("Customer", self.customer, "customer_name") | 			self.customer_name = frappe.db.get_value("Customer", self.party_name, "customer_name") | ||||||
| 		elif self.lead: | 		elif self.party_name and self.opportunity_from == 'Lead': | ||||||
| 			lead_name, company_name = frappe.db.get_value("Lead", self.lead, ["lead_name", "company_name"]) | 			lead_name, company_name = frappe.db.get_value("Lead", self.party_name, ["lead_name", "company_name"]) | ||||||
| 			self.customer_name = company_name or lead_name | 			self.customer_name = company_name or lead_name | ||||||
| 
 | 
 | ||||||
| 	def on_update(self): | 	def on_update(self): | ||||||
| @ -161,16 +157,16 @@ class Opportunity(TransactionBase): | |||||||
| 		opts.description = "" | 		opts.description = "" | ||||||
| 		opts.contact_date = self.contact_date | 		opts.contact_date = self.contact_date | ||||||
| 
 | 
 | ||||||
| 		if self.customer: | 		if self.party_name and self.opportunity_from == 'Customer': | ||||||
| 			if self.contact_person: | 			if self.contact_person: | ||||||
| 				opts.description = 'Contact '+cstr(self.contact_person) | 				opts.description = 'Contact '+cstr(self.contact_person) | ||||||
| 			else: | 			else: | ||||||
| 				opts.description = 'Contact customer '+cstr(self.customer) | 				opts.description = 'Contact customer '+cstr(self.party_name) | ||||||
| 		elif self.lead: | 		elif self.party_name and self.opportunity_from == 'Lead': | ||||||
| 			if self.contact_display: | 			if self.contact_display: | ||||||
| 				opts.description = 'Contact '+cstr(self.contact_display) | 				opts.description = 'Contact '+cstr(self.contact_display) | ||||||
| 			else: | 			else: | ||||||
| 				opts.description = 'Contact lead '+cstr(self.lead) | 				opts.description = 'Contact lead '+cstr(self.party_name) | ||||||
| 
 | 
 | ||||||
| 		opts.subject = opts.description | 		opts.subject = opts.description | ||||||
| 		opts.description += '. By : ' + cstr(self.contact_by) | 		opts.description += '. By : ' + cstr(self.contact_by) | ||||||
| @ -195,17 +191,6 @@ class Opportunity(TransactionBase): | |||||||
| 			for key in item_fields: | 			for key in item_fields: | ||||||
| 				if not d.get(key): d.set(key, item.get(key)) | 				if not d.get(key): d.set(key, item.get(key)) | ||||||
| 
 | 
 | ||||||
| 	def validate_lead_cust(self): |  | ||||||
| 		if self.enquiry_from == 'Lead': |  | ||||||
| 			if not self.lead: |  | ||||||
| 				frappe.throw(_("Lead must be set if Opportunity is made from Lead")) |  | ||||||
| 			else: |  | ||||||
| 				self.customer = None |  | ||||||
| 		elif self.enquiry_from == 'Customer': |  | ||||||
| 			if not self.customer: |  | ||||||
| 				msgprint(_("Customer is mandatory if 'Opportunity From' is selected as Customer"), raise_exception=1) |  | ||||||
| 			else: |  | ||||||
| 				self.lead = None |  | ||||||
| 
 | 
 | ||||||
| @frappe.whitelist() | @frappe.whitelist() | ||||||
| def get_item_details(item_code): | def get_item_details(item_code): | ||||||
| @ -227,8 +212,11 @@ def make_quotation(source_name, target_doc=None): | |||||||
| 		quotation = frappe.get_doc(target) | 		quotation = frappe.get_doc(target) | ||||||
| 
 | 
 | ||||||
| 		company_currency = frappe.get_cached_value('Company',  quotation.company,  "default_currency") | 		company_currency = frappe.get_cached_value('Company',  quotation.company,  "default_currency") | ||||||
| 		party_account_currency = get_party_account_currency("Customer", quotation.customer, | 
 | ||||||
| 			quotation.company) if quotation.customer else company_currency | 		if quotation.quotation_to == 'Customer' and quotation.party_name: | ||||||
|  | 			party_account_currency = get_party_account_currency("Customer", quotation.party_name, quotation.company) | ||||||
|  | 		else: | ||||||
|  | 			party_account_currency = company_currency | ||||||
| 
 | 
 | ||||||
| 		quotation.currency = party_account_currency or company_currency | 		quotation.currency = party_account_currency or company_currency | ||||||
| 
 | 
 | ||||||
| @ -254,7 +242,7 @@ def make_quotation(source_name, target_doc=None): | |||||||
| 		"Opportunity": { | 		"Opportunity": { | ||||||
| 			"doctype": "Quotation", | 			"doctype": "Quotation", | ||||||
| 			"field_map": { | 			"field_map": { | ||||||
| 				"enquiry_from": "quotation_to", | 				"opportunity_from": "quotation_to", | ||||||
| 				"opportunity_type": "order_type", | 				"opportunity_type": "order_type", | ||||||
| 				"name": "enq_no", | 				"name": "enq_no", | ||||||
| 			} | 			} | ||||||
| @ -340,11 +328,11 @@ def make_opportunity_from_communication(communication, ignore_communication_link | |||||||
| 	if not lead: | 	if not lead: | ||||||
| 		lead = make_lead_from_communication(communication, ignore_communication_links=True) | 		lead = make_lead_from_communication(communication, ignore_communication_links=True) | ||||||
| 
 | 
 | ||||||
| 	enquiry_from = "Lead" | 	opportunity_from = "Lead" | ||||||
| 
 | 
 | ||||||
| 	opportunity = frappe.get_doc({ | 	opportunity = frappe.get_doc({ | ||||||
| 		"doctype": "Opportunity", | 		"doctype": "Opportunity", | ||||||
| 		"enquiry_from": enquiry_from, | 		"opportunity_from": opportunity_from, | ||||||
| 		"lead": lead | 		"lead": lead | ||||||
| 	}).insert(ignore_permissions=True) | 	}).insert(ignore_permissions=True) | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,5 +1,5 @@ | |||||||
| frappe.listview_settings['Opportunity'] = { | frappe.listview_settings['Opportunity'] = { | ||||||
| 	add_fields: ["customer_name", "opportunity_type", "enquiry_from", "status"], | 	add_fields: ["customer_name", "opportunity_type", "opportunity_from", "status"], | ||||||
| 	get_indicator: function(doc) { | 	get_indicator: function(doc) { | ||||||
| 		var indicator = [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status]; | 		var indicator = [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status]; | ||||||
| 		if(doc.status=="Quotation") { | 		if(doc.status=="Quotation") { | ||||||
| @ -17,5 +17,13 @@ frappe.listview_settings['Opportunity'] = { | |||||||
| 		listview.page.add_menu_item(__("Set as Closed"), function() { | 		listview.page.add_menu_item(__("Set as Closed"), function() { | ||||||
| 			listview.call_for_selected_items(method, {"status": "Closed"}); | 			listview.call_for_selected_items(method, {"status": "Closed"}); | ||||||
| 		}); | 		}); | ||||||
|  | 
 | ||||||
|  | 		listview.page.fields_dict.opportunity_from.get_query = function() { | ||||||
|  | 			return { | ||||||
|  | 				"filters": { | ||||||
|  | 					"name": ["in", ["Customer", "Lead"]], | ||||||
|  | 				} | ||||||
|  | 			}; | ||||||
|  | 		}; | ||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ QUnit.test("test: opportunity", function (assert) { | |||||||
| 		() => frappe.timeout(1), | 		() => frappe.timeout(1), | ||||||
| 		() => frappe.click_button('New'), | 		() => frappe.click_button('New'), | ||||||
| 		() => frappe.timeout(1), | 		() => frappe.timeout(1), | ||||||
| 		() => cur_frm.set_value('enquiry_from', 'Customer'), | 		() => cur_frm.set_value('opportunity_from', 'Customer'), | ||||||
| 		() => cur_frm.set_value('customer', 'Test Customer 1'), | 		() => cur_frm.set_value('customer', 'Test Customer 1'), | ||||||
| 
 | 
 | ||||||
| 		// check items
 | 		// check items
 | ||||||
|  | |||||||
| @ -38,13 +38,13 @@ class TestOpportunity(unittest.TestCase): | |||||||
| 		# new lead should be created against the new.opportunity@example.com | 		# new lead should be created against the new.opportunity@example.com | ||||||
| 		opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) | 		opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) | ||||||
| 
 | 
 | ||||||
| 		self.assertTrue(opp_doc.lead) | 		self.assertTrue(opp_doc.party_name) | ||||||
| 		self.assertEqual(opp_doc.enquiry_from, "Lead") | 		self.assertEqual(opp_doc.opportunity_from, "Lead") | ||||||
| 		self.assertEqual(frappe.db.get_value("Lead", opp_doc.lead, "email_id"), | 		self.assertEqual(frappe.db.get_value("Lead", opp_doc.party_name, "email_id"), | ||||||
| 			new_lead_email_id) | 			new_lead_email_id) | ||||||
| 
 | 
 | ||||||
| 		# create new customer and create new contact against 'new.opportunity@example.com' | 		# create new customer and create new contact against 'new.opportunity@example.com' | ||||||
| 		customer = make_customer(opp_doc.lead).insert(ignore_permissions=True) | 		customer = make_customer(opp_doc.party_name).insert(ignore_permissions=True) | ||||||
| 		frappe.get_doc({ | 		frappe.get_doc({ | ||||||
| 			"doctype": "Contact", | 			"doctype": "Contact", | ||||||
| 			"email_id": new_lead_email_id, | 			"email_id": new_lead_email_id, | ||||||
| @ -56,10 +56,9 @@ class TestOpportunity(unittest.TestCase): | |||||||
| 		}).insert(ignore_permissions=True) | 		}).insert(ignore_permissions=True) | ||||||
| 
 | 
 | ||||||
| 		opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) | 		opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) | ||||||
| 		self.assertTrue(opp_doc.customer) | 		self.assertTrue(opp_doc.party_name) | ||||||
| 		self.assertEqual(opp_doc.enquiry_from, "Customer") | 		self.assertEqual(opp_doc.opportunity_from, "Customer") | ||||||
| 		self.assertEqual(opp_doc.customer, customer.name) | 		self.assertEqual(opp_doc.party_name, customer.name) | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
| def make_opportunity(**args): | def make_opportunity(**args): | ||||||
| 	args = frappe._dict(args) | 	args = frappe._dict(args) | ||||||
| @ -67,17 +66,17 @@ def make_opportunity(**args): | |||||||
| 	opp_doc = frappe.get_doc({ | 	opp_doc = frappe.get_doc({ | ||||||
| 		"doctype": "Opportunity", | 		"doctype": "Opportunity", | ||||||
| 		"company": args.company or "_Test Company", | 		"company": args.company or "_Test Company", | ||||||
| 		"enquiry_from": args.enquiry_from or "Customer", | 		"opportunity_from": args.opportunity_from or "Customer", | ||||||
| 		"opportunity_type": "Sales", | 		"opportunity_type": "Sales", | ||||||
| 		"with_items": args.with_items or 0, | 		"with_items": args.with_items or 0, | ||||||
| 		"transaction_date": today() | 		"transaction_date": today() | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	if opp_doc.enquiry_from == 'Customer': | 	if opp_doc.opportunity_from == 'Customer': | ||||||
| 		opp_doc.customer = args.customer or "_Test Customer" | 		opp_doc.party_name= args.customer or "_Test Customer" | ||||||
| 
 | 
 | ||||||
| 	if opp_doc.enquiry_from == 'Lead': | 	if opp_doc.opportunity_from == 'Lead': | ||||||
| 		opp_doc.lead = args.lead or "_T-Lead-00001" | 		opp_doc.party_name = args.lead or "_T-Lead-00001" | ||||||
| 
 | 
 | ||||||
| 	if args.with_items: | 	if args.with_items: | ||||||
| 		opp_doc.append('items', { | 		opp_doc.append('items', { | ||||||
|  | |||||||
| @ -2,9 +2,9 @@ | |||||||
| 	{ | 	{ | ||||||
| 		"doctype": "Opportunity", | 		"doctype": "Opportunity", | ||||||
| 		"name": "_Test Opportunity 1", | 		"name": "_Test Opportunity 1", | ||||||
| 		"enquiry_from": "Lead", | 		"opportunity_from": "Lead", | ||||||
| 		"enquiry_type": "Sales", | 		"enquiry_type": "Sales", | ||||||
| 		"lead": "_T-Lead-00001", | 		"party_name": "_T-Lead-00001", | ||||||
| 		"transaction_date": "2013-12-12", | 		"transaction_date": "2013-12-12", | ||||||
| 		"items": [{ | 		"items": [{ | ||||||
| 			"item_name": "Test Item", | 			"item_name": "Test Item", | ||||||
|  | |||||||
| @ -87,7 +87,7 @@ def get_employee_emails_for_popup(communication_medium): | |||||||
| 		'parent': communication_medium, | 		'parent': communication_medium, | ||||||
| 		'from_time': ['<=', now_time], | 		'from_time': ['<=', now_time], | ||||||
| 		'to_time': ['>=', now_time], | 		'to_time': ['>=', now_time], | ||||||
| 	}, fields=['employee_group'], debug=1) | 	}, fields=['employee_group']) | ||||||
| 
 | 
 | ||||||
| 	available_employee_groups = tuple([emp.employee_group for emp in available_employee_groups]) | 	available_employee_groups = tuple([emp.employee_group for emp in available_employee_groups]) | ||||||
| 
 | 
 | ||||||
|  | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user