fix: rewrite to allow referring to existing sections and reduce to single amount column
This commit is contained in:
		
							parent
							
								
									1c37390899
								
							
						
					
					
						commit
						2cb0da8780
					
				| @ -43,7 +43,7 @@ frappe.query_reports["Tax Detail"] = { | |||||||
| 			fieldname: "mode", | 			fieldname: "mode", | ||||||
| 			label: __("Mode"), | 			label: __("Mode"), | ||||||
| 			fieldtype: "Read Only", | 			fieldtype: "Read Only", | ||||||
| 			default: "run", | 			default: "edit", | ||||||
| 			hidden: 1, | 			hidden: 1, | ||||||
| 			reqd: 1 | 			reqd: 1 | ||||||
| 		} | 		} | ||||||
| @ -83,12 +83,12 @@ erpnext.TaxDetail = class TaxDetail { | |||||||
| 		// The last thing to run after datatable_render in refresh()
 | 		// The last thing to run after datatable_render in refresh()
 | ||||||
| 		this.super.show_footer_message.apply(this.qr); | 		this.super.show_footer_message.apply(this.qr); | ||||||
| 		if (this.qr.report_name !== 'Tax Detail') { | 		if (this.qr.report_name !== 'Tax Detail') { | ||||||
| 			this.set_value_options(); |  | ||||||
| 			this.show_help(); | 			this.show_help(); | ||||||
| 			if (this.loading) { | 			if (this.loading) { | ||||||
| 				this.set_section(''); | 				this.set_section(''); | ||||||
|  | 			} else { | ||||||
|  | 				this.reload_component(''); | ||||||
| 			} | 			} | ||||||
| 			this.reload_filter(); |  | ||||||
| 		} | 		} | ||||||
| 		this.loading = false; | 		this.loading = false; | ||||||
| 	} | 	} | ||||||
| @ -134,6 +134,7 @@ erpnext.TaxDetail = class TaxDetail { | |||||||
| 		return new_items; | 		return new_items; | ||||||
| 	} | 	} | ||||||
| 	save_report() { | 	save_report() { | ||||||
|  | 		this.check_datatable(); | ||||||
| 		if (this.qr.report_name !== 'Tax Detail') { | 		if (this.qr.report_name !== 'Tax Detail') { | ||||||
| 			frappe.call({ | 			frappe.call({ | ||||||
| 				method:'erpnext.accounts.report.tax_detail.tax_detail.save_custom_report', | 				method:'erpnext.accounts.report.tax_detail.tax_detail.save_custom_report', | ||||||
| @ -152,55 +153,13 @@ erpnext.TaxDetail = class TaxDetail { | |||||||
| 			}); | 			}); | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	set_value_options() { | 	check_datatable() { | ||||||
| 		// May be run with no columns or data
 | 		if (!this.qr.datatable) { | ||||||
| 		if (this.qr.columns) { | 			frappe.throw(__('Please change the date range to load data first')); | ||||||
| 			this.fieldname_lookup = {}; |  | ||||||
| 			this.label_lookup = {}; |  | ||||||
| 			this.qr.columns.forEach((col, index) => { |  | ||||||
| 				if (col['fieldtype'] == "Currency") { |  | ||||||
| 					this.fieldname_lookup[col['label']] = col['fieldname']; |  | ||||||
| 					this.label_lookup[col['fieldname']] = col['label']; |  | ||||||
| 				} |  | ||||||
| 			}); |  | ||||||
| 			const options = Object.keys(this.fieldname_lookup); |  | ||||||
| 			this.controls['value_field'].$wrapper.find("select").empty().add_options(options); |  | ||||||
| 			this.controls['value_field'].set_input(options[0]); |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	set_value_label_from_filter() { |  | ||||||
| 		const section_name = this.controls['section_name'].get_input_value(); |  | ||||||
| 		const fidx = this.controls['filter_index'].get_input_value(); |  | ||||||
| 		if (section_name && fidx) { |  | ||||||
| 			const fieldname = this.sections[section_name][fidx]['fieldname']; |  | ||||||
| 			this.controls['value_field'].set_input(this.label_lookup[fieldname]); |  | ||||||
| 		} else { |  | ||||||
| 			this.controls['value_field'].set_input(Object.keys(this.fieldname_lookup)[0]); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	get_value_fieldname() { |  | ||||||
| 		const curlabel = this.controls['value_field'].get_input_value(); |  | ||||||
| 		return this.fieldname_lookup[curlabel]; |  | ||||||
| 	} |  | ||||||
| 	new_section(label) { |  | ||||||
| 		const dialog = new frappe.ui.Dialog({ |  | ||||||
| 			title: label, |  | ||||||
| 			fields: [{ |  | ||||||
| 				fieldname: 'data', |  | ||||||
| 				label: label, |  | ||||||
| 				fieldtype: 'Data' |  | ||||||
| 			}], |  | ||||||
| 			primary_action_label: label, |  | ||||||
| 			primary_action: (values) => { |  | ||||||
| 				dialog.hide(); |  | ||||||
| 				this.set_section(values.data); |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
| 		dialog.show(); |  | ||||||
| 	} |  | ||||||
| 	set_section(name) { | 	set_section(name) { | ||||||
| 		// Sets the given section name and then reloads the data
 | 		// Sets the given section name and then reloads the data
 | ||||||
| 		this.controls['filter_index'].set_input(''); |  | ||||||
| 		if (name && !this.sections[name]) { | 		if (name && !this.sections[name]) { | ||||||
| 			this.sections[name] = {}; | 			this.sections[name] = {}; | ||||||
| 		} | 		} | ||||||
| @ -225,43 +184,49 @@ erpnext.TaxDetail = class TaxDetail { | |||||||
| 		if (refresh) { | 		if (refresh) { | ||||||
| 			this.qr.refresh(); | 			this.qr.refresh(); | ||||||
| 		} | 		} | ||||||
| 		this.reload_filter(); | 		this.reload_component(''); | ||||||
| 	} | 	} | ||||||
| 	reload_filter() { | 	reload_component(component_name) { | ||||||
| 		const section_name = this.controls['section_name'].get_input_value(); | 		const section_name = this.controls['section_name'].get_input_value(); | ||||||
| 		if (section_name) { | 		if (section_name) { | ||||||
| 			let fidx = this.controls['filter_index'].get_input_value(); | 			const section = this.sections[section_name]; | ||||||
| 			let section = this.sections[section_name]; | 			const component_names = Object.keys(section); | ||||||
| 			let fidxs = Object.keys(section); | 			component_names.unshift(''); | ||||||
| 			fidxs.unshift(''); | 			this.controls['component'].$wrapper.find("select").empty().add_options(component_names); | ||||||
| 			this.controls['filter_index'].$wrapper.find("select").empty().add_options(fidxs); | 			this.controls['component'].set_input(component_name); | ||||||
| 			this.controls['filter_index'].set_input(fidx); | 			if (component_name) { | ||||||
|  | 				this.controls['component_type'].set_input(section[component_name].type); | ||||||
|  | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			this.controls['filter_index'].$wrapper.find("select").empty(); | 			this.controls['component'].$wrapper.find("select").empty(); | ||||||
| 			this.controls['filter_index'].set_input(''); | 			this.controls['component'].set_input(''); | ||||||
| 		} | 		} | ||||||
| 		this.set_table_filters(); | 		this.set_table_filters(); | ||||||
| 	} | 	} | ||||||
| 	set_table_filters() { | 	set_table_filters() { | ||||||
| 		let filters = {}; | 		let filters = {}; | ||||||
| 		const section_name = this.controls['section_name'].get_input_value(); | 		const section_name = this.controls['section_name'].get_input_value(); | ||||||
| 		const fidx = this.controls['filter_index'].get_input_value(); | 		const component_name = this.controls['component'].get_input_value(); | ||||||
| 		if (section_name && fidx) { | 		if (section_name && component_name) { | ||||||
| 			filters = this.sections[section_name][fidx]['filters']; | 			const component_type = this.sections[section_name][component_name].type; | ||||||
|  | 			if (component_type === 'filter') { | ||||||
|  | 				filters = this.sections[section_name][component_name]['filters']; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 		this.setAppliedFilters(filters); | 		this.setAppliedFilters(filters); | ||||||
| 		this.set_value_label_from_filter(); |  | ||||||
| 	} | 	} | ||||||
| 	setAppliedFilters(filters) { | 	setAppliedFilters(filters) { | ||||||
| 		Array.from(this.qr.datatable.header.querySelectorAll('.dt-filter')).map(function setFilters(input) { | 		if (this.qr.datatable) { | ||||||
| 			let idx = input.dataset.colIndex; | 			Array.from(this.qr.datatable.header.querySelectorAll('.dt-filter')).map(function setFilters(input) { | ||||||
| 			if (filters[idx]) { | 				let idx = input.dataset.colIndex; | ||||||
| 				input.value = filters[idx]; | 				if (filters[idx]) { | ||||||
| 			} else { | 					input.value = filters[idx]; | ||||||
| 				input.value = null; | 				} else { | ||||||
| 			} | 					input.value = null; | ||||||
| 		}); | 				} | ||||||
| 		this.qr.datatable.columnmanager.applyFilter(filters); | 			}); | ||||||
|  | 			this.qr.datatable.columnmanager.applyFilter(filters); | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	delete(name, type) { | 	delete(name, type) { | ||||||
| 		if (type === 'section') { | 		if (type === 'section') { | ||||||
| @ -269,11 +234,10 @@ erpnext.TaxDetail = class TaxDetail { | |||||||
| 			const new_section = Object.keys(this.sections)[0] || ''; | 			const new_section = Object.keys(this.sections)[0] || ''; | ||||||
| 			this.set_section(new_section); | 			this.set_section(new_section); | ||||||
| 		} | 		} | ||||||
| 		if (type === 'filter') { | 		if (type === 'component') { | ||||||
| 			const cur_section = this.controls['section_name'].get_input_value(); | 			const cur_section = this.controls['section_name'].get_input_value(); | ||||||
| 			delete this.sections[cur_section][name]; | 			delete this.sections[cur_section][name]; | ||||||
| 			this.controls['filter_index'].set_input(''); | 			this.reload_component(''); | ||||||
| 			this.reload_filter(); |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	create_controls() { | 	create_controls() { | ||||||
| @ -293,7 +257,13 @@ erpnext.TaxDetail = class TaxDetail { | |||||||
| 			fieldtype: 'Button', | 			fieldtype: 'Button', | ||||||
| 			fieldname: 'new_section', | 			fieldname: 'new_section', | ||||||
| 			click: () => { | 			click: () => { | ||||||
| 				this.new_section(__('New Section')); | 				frappe.prompt({ | ||||||
|  | 					label: __('Section Name'), | ||||||
|  | 					fieldname: 'name', | ||||||
|  | 					fieldtype: 'Data' | ||||||
|  | 				}, (values) => { | ||||||
|  | 					this.set_section(values.name); | ||||||
|  | 				}); | ||||||
| 			} | 			} | ||||||
| 		}); | 		}); | ||||||
| 		controls['delete_section'] = this.qr.page.add_field({ | 		controls['delete_section'] = this.qr.page.add_field({ | ||||||
| @ -308,61 +278,87 @@ erpnext.TaxDetail = class TaxDetail { | |||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		}); | 		}); | ||||||
| 		controls['filter_index'] = this.qr.page.add_field({ | 		controls['component'] = this.qr.page.add_field({ | ||||||
| 			label: __('Filter'), | 			label: __('Component'), | ||||||
| 			fieldtype: 'Select', | 			fieldtype: 'Select', | ||||||
| 			fieldname: 'filter_index', | 			fieldname: 'component', | ||||||
| 			change: (e) => { | 			change: (e) => { | ||||||
| 				this.controls['filter_index'].set_input(this.controls['filter_index'].get_input_value()); | 				this.reload_component(this.controls['component'].get_input_value()); | ||||||
| 				this.set_table_filters(); |  | ||||||
| 			} | 			} | ||||||
| 		}); | 		}); | ||||||
| 		controls['add_filter'] = this.qr.page.add_field({ | 		controls['component_type'] = this.qr.page.add_field({ | ||||||
| 			label: __('Add Filter'), | 			label: __('Component Type'), | ||||||
|  | 			fieldtype: 'Select', | ||||||
|  | 			fieldname: 'component_type', | ||||||
|  | 			default: 'filter', | ||||||
|  | 			options: [ | ||||||
|  | 				{label: __('Filtered Row Subtotal'), value: 'filter'}, | ||||||
|  | 				{label: __('Section Subtotal'), value: 'section'} | ||||||
|  | 			] | ||||||
|  | 		}); | ||||||
|  | 		controls['add_component'] = this.qr.page.add_field({ | ||||||
|  | 			label: __('Add Component'), | ||||||
| 			fieldtype: 'Button', | 			fieldtype: 'Button', | ||||||
| 			fieldname: 'add_filter', | 			fieldname: 'add_component', | ||||||
| 			click: () => { | 			click: () => { | ||||||
|  | 				this.check_datatable(); | ||||||
| 				let section_name = this.controls['section_name'].get_input_value(); | 				let section_name = this.controls['section_name'].get_input_value(); | ||||||
| 				if (section_name) { | 				if (section_name) { | ||||||
| 					let prefix = 'Filter'; | 					const component_type = this.controls['component_type'].get_input_value(); | ||||||
| 					let data = { | 					let idx = 0; | ||||||
| 						filters: this.qr.datatable.columnmanager.getAppliedFilters(), | 					const names = Object.keys(this.sections[section_name]); | ||||||
| 						fieldname: this.get_value_fieldname() | 					if (names.length > 0) { | ||||||
|  | 						const idxs = names.map((key) => parseInt(key.match(/\d+$/)) || 0); | ||||||
|  | 						idx = Math.max(...idxs) + 1; | ||||||
| 					} | 					} | ||||||
| 					const fidxs = Object.keys(this.sections[section_name]); | 					const filters = this.qr.datatable.columnmanager.getAppliedFilters(); | ||||||
| 					let new_idx = prefix + '0'; | 					if (component_type === 'filter') { | ||||||
| 					if (fidxs.length > 0) { | 						const name = 'Filter' + idx.toString(); | ||||||
| 						const fiidxs = fidxs.map((key) => parseInt(key.replace(prefix, ''))); | 						let data = { | ||||||
| 						new_idx = prefix + (Math.max(...fiidxs) + 1).toString(); | 							type: component_type, | ||||||
|  | 							filters: filters | ||||||
|  | 						} | ||||||
|  | 						this.sections[section_name][name] = data; | ||||||
|  | 						this.reload_component(name); | ||||||
|  | 					} else if (component_type === 'section') { | ||||||
|  | 						if (filters && Object.keys(filters).length !== 0) { | ||||||
|  | 							frappe.show_alert({ | ||||||
|  | 								message: __('Column filters ignored'), | ||||||
|  | 								indicator: 'yellow' | ||||||
|  | 							}); | ||||||
|  | 						} | ||||||
|  | 						let data = { | ||||||
|  | 							type: component_type | ||||||
|  | 						} | ||||||
|  | 						frappe.prompt({ | ||||||
|  | 							label: __('Section'), | ||||||
|  | 							fieldname: 'section', | ||||||
|  | 							fieldtype: 'Select', | ||||||
|  | 							options: Object.keys(this.sections) | ||||||
|  | 						}, (values) => { | ||||||
|  | 							this.sections[section_name][values.section] = data; | ||||||
|  | 							this.reload_component(values.section); | ||||||
|  | 						}); | ||||||
|  | 					} else { | ||||||
|  | 						frappe.throw(__('Please select the Component Type first')); | ||||||
| 					} | 					} | ||||||
| 					this.sections[section_name][new_idx] = data; |  | ||||||
| 					this.controls['filter_index'].set_input(new_idx); |  | ||||||
| 					this.reload_filter(); |  | ||||||
| 				} else { | 				} else { | ||||||
| 					frappe.throw(__('Please add or select the Section first')); | 					frappe.throw(__('Please select the Section first')); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		}); | 		}); | ||||||
| 		controls['delete_filter'] = this.qr.page.add_field({ | 		controls['delete_component'] = this.qr.page.add_field({ | ||||||
| 			label: __('Delete Filter'), | 			label: __('Delete Component'), | ||||||
| 			fieldtype: 'Button', | 			fieldtype: 'Button', | ||||||
| 			fieldname: 'delete_filter', | 			fieldname: 'delete_component', | ||||||
| 			click: () => { | 			click: () => { | ||||||
| 				let cur_filter = this.controls['filter_index'].get_input_value(); | 				const component = this.controls['component'].get_input_value(); | ||||||
| 				if (cur_filter) { | 				if (component) { | ||||||
| 					frappe.confirm(__('Are you sure you want to delete filter ') + cur_filter + '?', | 					frappe.confirm(__('Are you sure you want to delete component ') + component + '?', | ||||||
| 					() => {this.delete(cur_filter, 'filter')}); | 					() => {this.delete(component, 'component')}); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		}); | 		}); | ||||||
| 		controls['value_field'] = this.qr.page.add_field({ |  | ||||||
| 			label: __('Value Column'), |  | ||||||
| 			fieldtype: 'Select', |  | ||||||
| 			fieldname: 'value_field', |  | ||||||
| 			change: (e) => { |  | ||||||
| 				this.controls['value_field'].set_input(this.controls['value_field'].get_input_value()); |  | ||||||
| 			} |  | ||||||
| 		}); |  | ||||||
| 		controls['save'] = this.qr.page.add_field({ | 		controls['save'] = this.qr.page.add_field({ | ||||||
| 			label: __('Save & Run'), | 			label: __('Save & Run'), | ||||||
| 			fieldtype: 'Button', | 			fieldtype: 'Button', | ||||||
| @ -380,13 +376,16 @@ erpnext.TaxDetail = class TaxDetail { | |||||||
| 		this.controls = controls; | 		this.controls = controls; | ||||||
| 	} | 	} | ||||||
| 	show_help() { | 	show_help() { | ||||||
| 		const help = __(`You can add multiple sections to your custom report using the New Section button above.
 | 		const help = __(`<strong>Help:</strong> Your custom report is built from General Ledger Entries within the date range.
 | ||||||
| 			To specify what data goes in each section, specify column filters in the data table, then save with Add Filter. | 			You can add multiple sections to the report using the New Section button. | ||||||
| 			Each section can have multiple filters added but be careful with the duplicated data rows. | 			Each component added to a section adds a subset of the data into the specified section. | ||||||
| 			You can specify which Currency column will be summed for each filter in the final report with the Value Column | 			Beware of duplicated data rows. | ||||||
| 			select box. Use the Show Detail box to see the data rows included in each section in the final report. | 			The Filtered Row component type saves the datatable column filters to specify the added data. | ||||||
| 			Once you're done, hit Save & Run.`);
 | 			The Section component type refers to the data in a previously defined section, but it cannot refer to its parent section. | ||||||
| 		this.qr.$report_footer.append(`<div class="col-md-12">${help}</div>`); | 			The Amount column is summed to give the section subtotal. | ||||||
|  | 			Use the Show Detail box to see the data rows included in each section in the final report. | ||||||
|  | 			Once finished, hit Save & Run. Report contributed by`);
 | ||||||
|  | 		this.qr.$report_footer.append(`<div class="col-md-12">${help}<a href="https://www.casesolved.co.uk"> Case Solved</a></div>`); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,7 +6,7 @@ from __future__ import unicode_literals | |||||||
| import frappe, json | import frappe, json | ||||||
| from frappe import _ | from frappe import _ | ||||||
| 
 | 
 | ||||||
| # NOTE: Payroll is implemented using Journal Entries which translate directly to GL Entries | # NOTE: Payroll is implemented using Journal Entries which are included as GL Entries | ||||||
| 
 | 
 | ||||||
| # field lists in multiple doctypes will be coalesced | # field lists in multiple doctypes will be coalesced | ||||||
| required_sql_fields = { | required_sql_fields = { | ||||||
| @ -60,23 +60,35 @@ def run_report(report_name, data): | |||||||
| 	columns = report_config.get('columns') | 	columns = report_config.get('columns') | ||||||
| 	sections = report_config.get('sections', {}) | 	sections = report_config.get('sections', {}) | ||||||
| 	show_detail = report_config.get('show_detail', 1) | 	show_detail = report_config.get('show_detail', 1) | ||||||
|  | 	report = {} | ||||||
| 	new_data = [] | 	new_data = [] | ||||||
| 	summary = [] | 	summary = [] | ||||||
| 	for section_name, section in sections.items(): | 	for section_name, section in sections.items(): | ||||||
| 		section_total = 0.0 | 		report[section_name] = {'rows': [], 'subtotal': 0.0} | ||||||
| 		for filt_name, filt in section.items(): | 		for component_name, component in section.items(): | ||||||
| 			value_field = filt['fieldname'] | 			if component['type'] == 'filter': | ||||||
| 			rmidxs = [] | 				for row in data: | ||||||
| 			for colno, filter_string in filt['filters'].items(): | 					matched = True | ||||||
| 				filter_field = columns[int(colno) - 1]['fieldname'] | 					for colno, filter_string in component['filters'].items(): | ||||||
| 				for i, row in enumerate(data): | 						filter_field = columns[int(colno) - 1]['fieldname'] | ||||||
| 					if not filter_match(row[filter_field], filter_string): | 						if not filter_match(row[filter_field], filter_string): | ||||||
| 						rmidxs += [i] | 							matched = False | ||||||
| 			rows = [row for i, row in enumerate(data) if i not in rmidxs] | 							break | ||||||
| 			section_total += subtotal(rows, value_field) | 					if matched: | ||||||
| 			if show_detail: new_data += rows | 						report[section_name]['rows'] += [row] | ||||||
| 		new_data += [ {columns[1]['fieldname']: section_name, columns[2]['fieldname']: section_total} ] | 						report[section_name]['subtotal'] += row['amount'] | ||||||
| 		summary += [ {'label': section_name, 'datatype': 'Currency', 'value': section_total} ] | 			if component['type'] == 'section': | ||||||
|  | 				if component_name == section_name: | ||||||
|  | 					frappe.throw(_("A report component cannot refer to its parent section: ") + section_name) | ||||||
|  | 				try: | ||||||
|  | 					report[section_name]['rows'] += report[component_name]['rows'] | ||||||
|  | 					report[section_name]['subtotal'] += report[component_name]['subtotal'] | ||||||
|  | 				except KeyError: | ||||||
|  | 					frappe.throw(_("A report component can only refer to an earlier section: ") + section_name) | ||||||
|  | 
 | ||||||
|  | 		if show_detail: new_data += report[section_name]['rows'] | ||||||
|  | 		new_data += [ {'voucher_no': section_name, 'amount': report[section_name]['subtotal']} ] | ||||||
|  | 		summary += [ {'label': section_name, 'datatype': 'Currency', 'value': report[section_name]['subtotal']} ] | ||||||
| 		if show_detail: new_data += [ {} ] | 		if show_detail: new_data += [ {} ] | ||||||
| 	return new_data or data, summary or None | 	return new_data or data, summary or None | ||||||
| 
 | 
 | ||||||
| @ -123,11 +135,6 @@ def filter_match(value, string): | |||||||
| 		if operator == '<': return True | 		if operator == '<': return True | ||||||
| 		return False | 		return False | ||||||
| 
 | 
 | ||||||
| def subtotal(data, field): |  | ||||||
| 	subtotal = 0.0 |  | ||||||
| 	for row in data: |  | ||||||
| 		subtotal += row[field] |  | ||||||
| 	return subtotal |  | ||||||
| 
 | 
 | ||||||
| abbrev = lambda dt: ''.join(l[0].lower() for l in dt.split(' ')) + '.' | abbrev = lambda dt: ''.join(l[0].lower() for l in dt.split(' ')) + '.' | ||||||
| doclist = lambda dt, dfs: [abbrev(dt) + f for f in dfs] | doclist = lambda dt, dfs: [abbrev(dt) + f for f in dfs] | ||||||
| @ -185,6 +192,9 @@ def modify_report_columns(doctype, field, column): | |||||||
| 		if field in ["item_tax_rate", "base_net_amount"]: | 		if field in ["item_tax_rate", "base_net_amount"]: | ||||||
| 			return None | 			return None | ||||||
| 
 | 
 | ||||||
|  | 	if doctype == "GL Entry" and field in ["debit", "credit"]: | ||||||
|  | 		column.update({"label": _("Amount"), "fieldname": "amount"}) | ||||||
|  | 
 | ||||||
| 	if field == "taxes_and_charges": | 	if field == "taxes_and_charges": | ||||||
| 		column.update({"label": _("Taxes and Charges Template")}) | 		column.update({"label": _("Taxes and Charges Template")}) | ||||||
| 	return column | 	return column | ||||||
| @ -193,6 +203,8 @@ def modify_report_data(data): | |||||||
| 	import json | 	import json | ||||||
| 	new_data = [] | 	new_data = [] | ||||||
| 	for line in data: | 	for line in data: | ||||||
|  | 		if line.debit: line.amount = -line.debit | ||||||
|  | 		else: line.amount = line.credit | ||||||
| 		# Remove Invoice GL Tax Entries and generate Tax entries from the invoice lines | 		# Remove Invoice GL Tax Entries and generate Tax entries from the invoice lines | ||||||
| 		if "Invoice" in line.voucher_type: | 		if "Invoice" in line.voucher_type: | ||||||
| 			if line.account_type != "Tax": | 			if line.account_type != "Tax": | ||||||
| @ -204,11 +216,11 @@ def modify_report_data(data): | |||||||
| 					tax_line.account_type = "Tax" | 					tax_line.account_type = "Tax" | ||||||
| 					tax_line.account = account | 					tax_line.account = account | ||||||
| 					if line.voucher_type == "Sales Invoice": | 					if line.voucher_type == "Sales Invoice": | ||||||
| 						line.credit = line.base_net_amount | 						line.amount = line.base_net_amount | ||||||
| 						tax_line.credit = line.base_net_amount * (rate / 100) | 						tax_line.amount = line.base_net_amount * (rate / 100) | ||||||
| 					if line.voucher_type == "Purchase Invoice": | 					if line.voucher_type == "Purchase Invoice": | ||||||
| 						line.debit = line.base_net_amount | 						line.amount = -line.base_net_amount | ||||||
| 						tax_line.debit = line.base_net_amount * (rate / 100) | 						tax_line.amount = -line.base_net_amount * (rate / 100) | ||||||
| 					new_data += [tax_line] | 					new_data += [tax_line] | ||||||
| 		else: | 		else: | ||||||
| 			new_data += [line] | 			new_data += [line] | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user