2019-07-30 11:22:24 +00:00
|
|
|
frappe.pages['bom-comparison-tool'].on_page_load = function(wrapper) {
|
|
|
|
var page = frappe.ui.make_app_page({
|
|
|
|
parent: wrapper,
|
|
|
|
title: __('BOM Comparison Tool'),
|
|
|
|
single_column: true
|
|
|
|
});
|
|
|
|
|
|
|
|
new erpnext.BOMComparisonTool(page);
|
|
|
|
}
|
|
|
|
|
|
|
|
erpnext.BOMComparisonTool = class BOMComparisonTool {
|
|
|
|
constructor(page) {
|
|
|
|
this.page = page;
|
|
|
|
this.make_form();
|
|
|
|
}
|
|
|
|
|
|
|
|
make_form() {
|
|
|
|
this.form = new frappe.ui.FieldGroup({
|
|
|
|
fields: [
|
|
|
|
{
|
|
|
|
label: __('BOM 1'),
|
|
|
|
fieldname: 'name1',
|
|
|
|
fieldtype: 'Link',
|
|
|
|
options: 'BOM',
|
2020-03-19 04:33:06 +00:00
|
|
|
change: () => this.fetch_and_render(),
|
|
|
|
get_query: () => {
|
|
|
|
return {
|
|
|
|
filters: {
|
|
|
|
"name": ["not in", [this.form.get_value("name2") || ""]]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-07-30 11:22:24 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'Column Break'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: __('BOM 2'),
|
|
|
|
fieldname: 'name2',
|
|
|
|
fieldtype: 'Link',
|
|
|
|
options: 'BOM',
|
2020-03-19 04:33:06 +00:00
|
|
|
change: () => this.fetch_and_render(),
|
|
|
|
get_query: () => {
|
|
|
|
return {
|
|
|
|
filters: {
|
|
|
|
"name": ["not in", [this.form.get_value("name1") || ""]]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-07-30 11:22:24 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'Section Break'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
fieldtype: 'HTML',
|
|
|
|
fieldname: 'preview'
|
|
|
|
}
|
|
|
|
],
|
|
|
|
body: this.page.body
|
|
|
|
});
|
|
|
|
this.form.make();
|
|
|
|
}
|
|
|
|
|
|
|
|
fetch_and_render() {
|
|
|
|
let { name1, name2 } = this.form.get_values();
|
|
|
|
if (!(name1 && name2)) {
|
|
|
|
this.form.get_field('preview').html('');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// set working state
|
|
|
|
this.form.get_field('preview').html(`
|
|
|
|
<div class="text-muted margin-top">
|
|
|
|
${__("Fetching...")}
|
|
|
|
</div>
|
|
|
|
`);
|
|
|
|
|
|
|
|
frappe.call('erpnext.manufacturing.doctype.bom.bom.get_bom_diff', {
|
|
|
|
bom1: name1,
|
|
|
|
bom2: name2
|
|
|
|
}).then(r => {
|
|
|
|
let diff = r.message;
|
|
|
|
frappe.model.with_doctype('BOM', () => {
|
|
|
|
this.render('BOM', name1, name2, diff);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
render(doctype, name1, name2, diff) {
|
|
|
|
|
|
|
|
let change_html = (title, doctype, changed) => {
|
|
|
|
let values_changed = this.get_changed_values(doctype, changed)
|
|
|
|
.map(change => {
|
|
|
|
let [fieldname, value1, value2] = change;
|
|
|
|
return `
|
|
|
|
<tr>
|
|
|
|
<td>${frappe.meta.get_label(doctype, fieldname)}</td>
|
|
|
|
<td>${value1}</td>
|
|
|
|
<td>${value2}</td>
|
|
|
|
</tr>
|
|
|
|
`;
|
|
|
|
})
|
|
|
|
.join('');
|
|
|
|
|
|
|
|
return `
|
|
|
|
<h4 class="margin-top">${title}</h4>
|
|
|
|
<div>
|
|
|
|
<table class="table table-bordered">
|
|
|
|
<tr>
|
|
|
|
<th width="33%">${__('Field')}</th>
|
|
|
|
<th width="33%">${name1}</th>
|
|
|
|
<th width="33%">${name2}</th>
|
|
|
|
</tr>
|
|
|
|
${values_changed}
|
|
|
|
</table>
|
|
|
|
</div>
|
|
|
|
`;
|
|
|
|
}
|
|
|
|
|
|
|
|
let value_changes = change_html(__('Values Changed'), doctype, diff.changed);
|
|
|
|
|
|
|
|
let row_changes_by_fieldname = group_items(diff.row_changed, change => change[0]);
|
|
|
|
|
|
|
|
let table_changes = Object.keys(row_changes_by_fieldname).map(fieldname => {
|
|
|
|
let changes = row_changes_by_fieldname[fieldname];
|
|
|
|
let df = frappe.meta.get_docfield(doctype, fieldname);
|
|
|
|
|
|
|
|
let html = changes.map(change => {
|
2019-07-30 12:16:15 +00:00
|
|
|
let [fieldname,, item_code, changes] = change;
|
2019-07-30 11:22:24 +00:00
|
|
|
let df = frappe.meta.get_docfield(doctype, fieldname);
|
|
|
|
let child_doctype = df.options;
|
|
|
|
let values_changed = this.get_changed_values(child_doctype, changes);
|
|
|
|
|
|
|
|
return values_changed.map((change, i) => {
|
|
|
|
let [fieldname, value1, value2] = change;
|
2019-07-30 12:16:15 +00:00
|
|
|
let th = i === 0
|
|
|
|
? `<th rowspan="${values_changed.length}">${item_code}</th>`
|
|
|
|
: '';
|
2019-07-30 11:22:24 +00:00
|
|
|
return `
|
|
|
|
<tr>
|
2019-07-30 12:16:15 +00:00
|
|
|
${th}
|
2019-07-30 11:22:24 +00:00
|
|
|
<td>${frappe.meta.get_label(child_doctype, fieldname)}</td>
|
|
|
|
<td>${value1}</td>
|
|
|
|
<td>${value2}</td>
|
|
|
|
</tr>
|
|
|
|
`;
|
|
|
|
}).join('');
|
|
|
|
}).join('');
|
|
|
|
|
|
|
|
return `
|
|
|
|
<h4 class="margin-top">${__('Changes in {0}', [df.label])}</h4>
|
|
|
|
<table class="table table-bordered">
|
|
|
|
<tr>
|
|
|
|
<th width="25%">${__('Item Code')}</th>
|
|
|
|
<th width="25%">${__('Field')}</th>
|
|
|
|
<th width="25%">${name1}</th>
|
|
|
|
<th width="25%">${name2}</th>
|
|
|
|
</tr>
|
|
|
|
${html}
|
|
|
|
</table>
|
|
|
|
`;
|
|
|
|
}).join('');
|
|
|
|
|
|
|
|
let get_added_removed_html = (title, grouped_items) => {
|
|
|
|
return Object.keys(grouped_items).map(fieldname => {
|
|
|
|
let rows = grouped_items[fieldname];
|
|
|
|
let df = frappe.meta.get_docfield(doctype, fieldname);
|
|
|
|
let fields = frappe.meta.get_docfields(df.options)
|
|
|
|
.filter(df => df.in_list_view);
|
|
|
|
|
|
|
|
let html = rows.map(row => {
|
|
|
|
let [, doc] = row;
|
2019-07-30 12:16:15 +00:00
|
|
|
let cells = fields
|
|
|
|
.map(df => `<td>${doc[df.fieldname]}</td>`)
|
|
|
|
.join('');
|
|
|
|
return `<tr>${cells}</tr>`;
|
2019-07-30 11:22:24 +00:00
|
|
|
}).join('');
|
|
|
|
|
2019-07-30 12:16:15 +00:00
|
|
|
let header = fields.map(df => `<th>${df.label}</th>`).join('');
|
2019-07-30 11:22:24 +00:00
|
|
|
return `
|
|
|
|
<h4 class="margin-top">${$.format(title, [df.label])}</h4>
|
|
|
|
<table class="table table-bordered">
|
2019-07-30 12:16:15 +00:00
|
|
|
<tr>${header}</tr>
|
2019-07-30 11:22:24 +00:00
|
|
|
${html}
|
|
|
|
</table>
|
|
|
|
`;
|
|
|
|
}).join('');
|
2019-08-01 07:23:19 +00:00
|
|
|
};
|
2019-07-30 11:22:24 +00:00
|
|
|
|
|
|
|
let added_by_fieldname = group_items(diff.added, change => change[0]);
|
|
|
|
let removed_by_fieldname = group_items(diff.removed, change => change[0]);
|
|
|
|
|
|
|
|
let added_html = get_added_removed_html(__('Rows Added in {0}'), added_by_fieldname);
|
|
|
|
let removed_html = get_added_removed_html(__('Rows Removed in {0}'), removed_by_fieldname);
|
|
|
|
|
|
|
|
let html = `
|
|
|
|
${value_changes}
|
|
|
|
${table_changes}
|
|
|
|
${added_html}
|
|
|
|
${removed_html}
|
|
|
|
`;
|
|
|
|
|
|
|
|
this.form.get_field('preview').html(html);
|
|
|
|
}
|
|
|
|
|
|
|
|
get_changed_values(doctype, changed) {
|
|
|
|
return changed.filter(change => {
|
|
|
|
let [fieldname, value1, value2] = change;
|
|
|
|
if (!value1) value1 = '';
|
|
|
|
if (!value2) value2 = '';
|
|
|
|
if (value1 === value2) return false;
|
|
|
|
let df = frappe.meta.get_docfield(doctype, fieldname);
|
|
|
|
if (!df) return false;
|
|
|
|
if (df.hidden) return false;
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
}
|
2019-07-30 12:16:15 +00:00
|
|
|
};
|
2019-07-30 11:22:24 +00:00
|
|
|
|
|
|
|
function group_items(array, fn) {
|
|
|
|
return array.reduce((acc, item) => {
|
|
|
|
let key = fn(item);
|
|
|
|
acc[key] = acc[key] || [];
|
|
|
|
acc[key].push(item);
|
|
|
|
return acc;
|
|
|
|
}, {});
|
|
|
|
}
|