feat: Editable Sales Invoice
This commit is contained in:
		
							parent
							
								
									e543dca6a0
								
							
						
					
					
						commit
						30da6ab2c1
					
				| @ -64,6 +64,27 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e | |||||||
| 
 | 
 | ||||||
| 		this.frm.toggle_reqd("due_date", !this.frm.doc.is_return); | 		this.frm.toggle_reqd("due_date", !this.frm.doc.is_return); | ||||||
| 
 | 
 | ||||||
|  | 		if (this.frm.doc.repost_required) { | ||||||
|  | 			this.frm.set_intro(__("Accounting entries for this invoice needs to be reposted. Please click on 'Repost' button to update.")); | ||||||
|  | 			this.frm.add_custom_button(__('Repost Accounting Entries'), | ||||||
|  | 				() => { | ||||||
|  | 					this.frm.call({ | ||||||
|  | 						doc: this.frm.doc, | ||||||
|  | 						method: 'repost_accounting_entries', | ||||||
|  | 						freeze: true, | ||||||
|  | 						freeze_message: __('Reposting...'), | ||||||
|  | 						callback: (r) => { | ||||||
|  | 							if (!r.exc) { | ||||||
|  | 								frappe.msgprint(__('Accounting Entries are reposted')); | ||||||
|  | 								this.frm.trigger('refresh'); | ||||||
|  | 							} | ||||||
|  | 						} | ||||||
|  | 					}); | ||||||
|  | 				}); | ||||||
|  | 
 | ||||||
|  | 			$(`["${encodeURIComponent("Repost Accounting Entries")}"]`).css('color', 'red'); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if (this.frm.doc.is_return) { | 		if (this.frm.doc.is_return) { | ||||||
| 			this.frm.return_print_format = "Sales Invoice Return"; | 			this.frm.return_print_format = "Sales Invoice Return"; | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -207,6 +207,7 @@ | |||||||
|   "is_internal_customer", |   "is_internal_customer", | ||||||
|   "is_discounted", |   "is_discounted", | ||||||
|   "remarks", |   "remarks", | ||||||
|  |   "repost_required", | ||||||
|   "connections_tab" |   "connections_tab" | ||||||
|  ], |  ], | ||||||
|  "fields": [ |  "fields": [ | ||||||
| @ -1703,6 +1704,7 @@ | |||||||
|    "read_only": 1 |    "read_only": 1 | ||||||
|   }, |   }, | ||||||
|   { |   { | ||||||
|  |    "allow_on_submit": 1, | ||||||
|    "default": "No", |    "default": "No", | ||||||
|    "fieldname": "is_opening", |    "fieldname": "is_opening", | ||||||
|    "fieldtype": "Select", |    "fieldtype": "Select", | ||||||
| @ -2097,6 +2099,14 @@ | |||||||
|    "hide_seconds": 1, |    "hide_seconds": 1, | ||||||
|    "label": "Write Off", |    "label": "Write Off", | ||||||
|    "width": "50%" |    "width": "50%" | ||||||
|  |   }, | ||||||
|  |   { | ||||||
|  |    "default": "0", | ||||||
|  |    "fieldname": "repost_required", | ||||||
|  |    "fieldtype": "Check", | ||||||
|  |    "hidden": 1, | ||||||
|  |    "label": "Repost Required", | ||||||
|  |    "read_only": 1 | ||||||
|   } |   } | ||||||
|  ], |  ], | ||||||
|  "icon": "fa fa-file-text", |  "icon": "fa fa-file-text", | ||||||
| @ -2109,7 +2119,7 @@ | |||||||
|    "link_fieldname": "consolidated_invoice" |    "link_fieldname": "consolidated_invoice" | ||||||
|   } |   } | ||||||
|  ], |  ], | ||||||
|  "modified": "2022-10-11 13:07:36.488095", |  "modified": "2022-10-15 19:15:49.526529", | ||||||
|  "modified_by": "Administrator", |  "modified_by": "Administrator", | ||||||
|  "module": "Accounts", |  "module": "Accounts", | ||||||
|  "name": "Sales Invoice", |  "name": "Sales Invoice", | ||||||
|  | |||||||
| @ -11,6 +11,9 @@ from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form | |||||||
| 
 | 
 | ||||||
| import erpnext | import erpnext | ||||||
| from erpnext.accounts.deferred_revenue import validate_service_stop_date | from erpnext.accounts.deferred_revenue import validate_service_stop_date | ||||||
|  | from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( | ||||||
|  | 	get_accounting_dimensions, | ||||||
|  | ) | ||||||
| from erpnext.accounts.doctype.loyalty_program.loyalty_program import ( | from erpnext.accounts.doctype.loyalty_program.loyalty_program import ( | ||||||
| 	get_loyalty_program_details_with_points, | 	get_loyalty_program_details_with_points, | ||||||
| 	validate_loyalty_points, | 	validate_loyalty_points, | ||||||
| @ -100,13 +103,11 @@ class SalesInvoice(SellingController): | |||||||
| 		self.validate_debit_to_acc() | 		self.validate_debit_to_acc() | ||||||
| 		self.clear_unallocated_advances("Sales Invoice Advance", "advances") | 		self.clear_unallocated_advances("Sales Invoice Advance", "advances") | ||||||
| 		self.add_remarks() | 		self.add_remarks() | ||||||
| 		self.validate_write_off_account() |  | ||||||
| 		self.validate_account_for_change_amount() |  | ||||||
| 		self.validate_fixed_asset() | 		self.validate_fixed_asset() | ||||||
| 		self.set_income_account_for_fixed_assets() | 		self.set_income_account_for_fixed_assets() | ||||||
| 		self.validate_item_cost_centers() | 		self.validate_item_cost_centers() | ||||||
| 		self.validate_income_account() |  | ||||||
| 		self.check_conversion_rate() | 		self.check_conversion_rate() | ||||||
|  | 		self.validate_accounts() | ||||||
| 
 | 
 | ||||||
| 		validate_inter_company_party( | 		validate_inter_company_party( | ||||||
| 			self.doctype, self.customer, self.company, self.inter_company_invoice_reference | 			self.doctype, self.customer, self.company, self.inter_company_invoice_reference | ||||||
| @ -170,6 +171,11 @@ class SalesInvoice(SellingController): | |||||||
| 
 | 
 | ||||||
| 		self.reset_default_field_value("set_warehouse", "items", "warehouse") | 		self.reset_default_field_value("set_warehouse", "items", "warehouse") | ||||||
| 
 | 
 | ||||||
|  | 	def validate_accounts(self): | ||||||
|  | 		self.validate_write_off_account() | ||||||
|  | 		self.validate_account_for_change_amount() | ||||||
|  | 		self.validate_income_account() | ||||||
|  | 
 | ||||||
| 	def validate_fixed_asset(self): | 	def validate_fixed_asset(self): | ||||||
| 		for d in self.get("items"): | 		for d in self.get("items"): | ||||||
| 			if d.is_fixed_asset and d.meta.get_field("asset") and d.asset: | 			if d.is_fixed_asset and d.meta.get_field("asset") and d.asset: | ||||||
| @ -514,6 +520,64 @@ class SalesInvoice(SellingController): | |||||||
| 	def on_update(self): | 	def on_update(self): | ||||||
| 		self.set_paid_amount() | 		self.set_paid_amount() | ||||||
| 
 | 
 | ||||||
|  | 	def on_update_after_submit(self): | ||||||
|  | 		needs_repost = 0 | ||||||
|  | 		# Check if any field affecting accounting entry is altered | ||||||
|  | 		doc_before_update = self.get_doc_before_save() | ||||||
|  | 		accounting_dimensions = get_accounting_dimensions() | ||||||
|  | 
 | ||||||
|  | 		# Check if opening entry check updated | ||||||
|  | 		if doc_before_update.get("is_opening") != self.is_opening: | ||||||
|  | 			needs_repost = 1 | ||||||
|  | 
 | ||||||
|  | 		if not needs_repost: | ||||||
|  | 			# Parent Level Accounts excluding party account | ||||||
|  | 			for field in ( | ||||||
|  | 				"additional_discount_account", | ||||||
|  | 				"cash_bank_account", | ||||||
|  | 				"account_for_change_amount", | ||||||
|  | 				"write_off_account", | ||||||
|  | 				"loyalty_redemption_account", | ||||||
|  | 				"unrealized_profit_loss_account", | ||||||
|  | 			): | ||||||
|  | 				if doc_before_update.get(field) != self.get(field): | ||||||
|  | 					needs_repost = 1 | ||||||
|  | 					break | ||||||
|  | 
 | ||||||
|  | 			# Check for parent accounting dimensions | ||||||
|  | 			for dimension in accounting_dimensions: | ||||||
|  | 				if doc_before_update.get(dimension) != self.get(dimension): | ||||||
|  | 					needs_repost = 1 | ||||||
|  | 					break | ||||||
|  | 
 | ||||||
|  | 			# Check for parent level | ||||||
|  | 			for index, item in enumerate(self.get("items")): | ||||||
|  | 				for field in ( | ||||||
|  | 					"income_account", | ||||||
|  | 					"expense_account", | ||||||
|  | 					"discount_account", | ||||||
|  | 					"deferred_revenue_account", | ||||||
|  | 				): | ||||||
|  | 					if doc_before_update.get("items")[index].get(field) != item.get(field): | ||||||
|  | 						needs_repost = 1 | ||||||
|  | 						break | ||||||
|  | 
 | ||||||
|  | 				for dimension in accounting_dimensions: | ||||||
|  | 					if doc_before_update.get("items")[index].get(dimension) != item.get(dimension): | ||||||
|  | 						needs_repost = 1 | ||||||
|  | 						break | ||||||
|  | 
 | ||||||
|  | 		self.validate_accounts() | ||||||
|  | 		self.db_set("repost_required", needs_repost) | ||||||
|  | 
 | ||||||
|  | 	@frappe.whitelist() | ||||||
|  | 	def repost_accounting_entries(self): | ||||||
|  | 		self.docstatus = 2 | ||||||
|  | 		self.make_gl_entries_on_cancel() | ||||||
|  | 		self.docstatus = 1 | ||||||
|  | 		self.make_gl_entries() | ||||||
|  | 		self.db_set("repost_required", 0) | ||||||
|  | 
 | ||||||
| 	def set_paid_amount(self): | 	def set_paid_amount(self): | ||||||
| 		paid_amount = 0.0 | 		paid_amount = 0.0 | ||||||
| 		base_paid_amount = 0.0 | 		base_paid_amount = 0.0 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user