fix(client): stop duplicate form handlers; remove tax_type grid toggle
Split list-view code (frappe.listview_settings) out of the form client scripts into dedicated *_list.js files. The form files were registered under both doctype_js and doctype_list_js, so frappe.ui.form.on handlers were registered twice and form events (e.g. validate) fired twice, duplicating msgprint messages. - New: purchase_invoice_list.js, purchase_order_list.js, sales_order_list.js, sales_invoice_list.js, employee_list.js - doctype_list_js now points to the *_list.js files; journal_entry.js (list-only) removed from doctype_js - purchase_invoice.js: drop toggle_purchase_fields_visibility and the grid hidden/visible_columns hacks; tax_type is always visible and its mandatory state is driven by mandatory_depends_on. validate and tax calc now also honor agricultural_goods (equal trigger to purchase_type) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c29ac5bd1b
commit
4adde142b4
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -606,22 +606,23 @@ frappe.ui.form.on('Purchase Invoice', {
|
|||
// Make is_taxes_doc field read-only when document loads
|
||||
onload: function(frm) {
|
||||
frm.set_df_property('is_taxes_doc', 'read_only', 1);
|
||||
|
||||
// Set visibility of purchase fields on load
|
||||
toggle_purchase_fields_visibility(frm);
|
||||
},
|
||||
|
||||
purchase_type: function(frm) {
|
||||
// Toggle visibility of tax_type and purchase_tax_amount fields
|
||||
toggle_purchase_fields_visibility(frm);
|
||||
// tax_type / purchase_tax_amount are always-visible columns; their
|
||||
// mandatory state is driven by mandatory_depends_on (see custom_fields.py),
|
||||
// so no manual grid toggling is needed. Just refresh to show/hide buttons.
|
||||
frm.refresh();
|
||||
},
|
||||
|
||||
// Refresh to show/hide buttons when checkbox changes
|
||||
agricultural_goods: function(frm) {
|
||||
// Equal trigger to purchase_type for button visibility.
|
||||
frm.refresh();
|
||||
},
|
||||
|
||||
validate: function(frm) {
|
||||
// Client-side validation before save
|
||||
if (frm.doc.purchase_type && frm.doc.items) {
|
||||
if ((frm.doc.purchase_type || frm.doc.agricultural_goods) && frm.doc.items) {
|
||||
// Validate act_kind is selected
|
||||
if (!frm.doc.act_kind) {
|
||||
frappe.msgprint({
|
||||
|
|
@ -676,37 +677,14 @@ frappe.ui.form.on('Purchase Invoice Item', {
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Toggle visibility of purchase fields in items table
|
||||
*/
|
||||
function toggle_purchase_fields_visibility(frm) {
|
||||
// Get the grid object
|
||||
let grid = frm.fields_dict.items.grid;
|
||||
|
||||
if (frm.doc.purchase_type) {
|
||||
// Show fields
|
||||
grid.update_docfield_property('tax_type', 'hidden', 0);
|
||||
grid.update_docfield_property('purchase_tax_amount', 'hidden', 0);
|
||||
grid.update_docfield_property('tax_type', 'reqd', 1); // Make required
|
||||
} else {
|
||||
// Hide fields
|
||||
grid.update_docfield_property('tax_type', 'hidden', 1);
|
||||
grid.update_docfield_property('purchase_tax_amount', 'hidden', 1);
|
||||
grid.update_docfield_property('tax_type', 'reqd', 0); // Not required
|
||||
}
|
||||
|
||||
// Refresh grid to apply changes
|
||||
grid.refresh();
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate purchase tax amount (5% if Taxable)
|
||||
*/
|
||||
function calculate_purchase_tax(frm, cdt, cdn) {
|
||||
let item = locals[cdt][cdn];
|
||||
|
||||
// Only calculate if parent has purchase_type checked
|
||||
if (!frm.doc.purchase_type) {
|
||||
// Only calculate if parent has purchase_type or agricultural_goods checked
|
||||
if (!frm.doc.purchase_type && !frm.doc.agricultural_goods) {
|
||||
frappe.model.set_value(cdt, cdn, 'purchase_tax_amount', 0);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1053,28 +1031,8 @@ function get_status_color(status) {
|
|||
return colors[status] || 'gray';
|
||||
}
|
||||
|
||||
frappe.listview_settings['Purchase Invoice'] = {
|
||||
onload: function(listview) {
|
||||
// Add field as column (proper way)
|
||||
listview.columns.push({
|
||||
type: 'Check',
|
||||
df: {
|
||||
label: __('Tax Doc'),
|
||||
fieldname: 'is_taxes_doc'
|
||||
},
|
||||
width: 120
|
||||
});
|
||||
|
||||
// Force refresh list with new column
|
||||
listview.refresh();
|
||||
},
|
||||
|
||||
// Format is_taxes_doc field display
|
||||
formatters: {
|
||||
is_taxes_doc: function(value) {
|
||||
return value ?
|
||||
`<span class="indicator-pill green" title="${__('Tax Document')}"></span>` :
|
||||
`<span class="indicator-pill gray" title="${__('Regular Document')}"></span>`;
|
||||
}
|
||||
}
|
||||
};
|
||||
// NOTE: list view customizations live in client/purchase_invoice_list.js,
|
||||
// loaded via the doctype_list_js hook. This file is form-only (doctype_js).
|
||||
// Keeping frappe.ui.form.on(...) out of the list bundle prevents the form
|
||||
// handlers (e.g. validate) from being registered twice, which previously
|
||||
// caused validation messages to appear duplicated.
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
// List view customizations for Purchase Invoice.
|
||||
// Loaded via the `doctype_list_js` hook ONLY. Do not add frappe.ui.form.on(...)
|
||||
// handlers here — this file runs in list context and any form handler defined
|
||||
// here would be registered a second time (in addition to client/purchase_invoice.js
|
||||
// loaded by doctype_js), causing form events like `validate` to fire twice.
|
||||
|
||||
frappe.listview_settings['Purchase Invoice'] = {
|
||||
onload: function(listview) {
|
||||
// Add field as column (proper way)
|
||||
listview.columns.push({
|
||||
type: 'Check',
|
||||
df: {
|
||||
label: __('Tax Doc'),
|
||||
fieldname: 'is_taxes_doc'
|
||||
},
|
||||
width: 120
|
||||
});
|
||||
|
||||
// Force refresh list with new column
|
||||
listview.refresh();
|
||||
},
|
||||
|
||||
// Format is_taxes_doc field display
|
||||
formatters: {
|
||||
is_taxes_doc: function(value) {
|
||||
return value ?
|
||||
`<span class="indicator-pill green" title="${__('Tax Document')}"></span>` :
|
||||
`<span class="indicator-pill gray" title="${__('Regular Document')}"></span>`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1567,56 +1567,6 @@ frappe.ui.form.on('Purchase Order', {
|
|||
}
|
||||
});
|
||||
|
||||
// Purchase Order List
|
||||
frappe.listview_settings['Purchase Order'] = {
|
||||
add_fields: ['supplier', 'transaction_date', 'status', 'docstatus', 'is_taxes_doc', 'taxes_doc'],
|
||||
|
||||
get_indicator: function(doc) {
|
||||
if (doc.status === 'Closed') {
|
||||
return [__('Closed'), 'green', 'status,=,Closed'];
|
||||
} else if (doc.status === 'On Hold') {
|
||||
return [__('On Hold'), 'orange', 'status,=,On Hold'];
|
||||
} else if (doc.status === 'Delivered') {
|
||||
return [__('Delivered'), 'green', 'status,=,Delivered'];
|
||||
} else if (doc.docstatus == 1) {
|
||||
return [__('To Receive and Bill'), 'orange', 'docstatus,=,1'];
|
||||
} else if (doc.docstatus == 0) {
|
||||
return [__('Draft'), 'red', 'docstatus,=,0'];
|
||||
} else if (doc.docstatus == 2) {
|
||||
return [__('Cancelled'), 'grey', 'docstatus,=,2'];
|
||||
}
|
||||
},
|
||||
|
||||
onload: function(listview) {
|
||||
listview.page.add_inner_button(__('Import from E-Taxes'), function() {
|
||||
ETaxes.import.showDialog();
|
||||
}, __('E-Taxes'));
|
||||
|
||||
listview.page.add_inner_button(__('Open Settings'), function() {
|
||||
frappe.set_route('Form', 'E-Taxes Settings');
|
||||
}, __('E-Taxes'));
|
||||
|
||||
listview.columns.push({
|
||||
type: 'Check',
|
||||
df: {
|
||||
label: __('Tax Doc'),
|
||||
fieldname: 'is_taxes_doc'
|
||||
},
|
||||
width: 120
|
||||
});
|
||||
|
||||
listview.refresh();
|
||||
},
|
||||
|
||||
formatters: {
|
||||
is_taxes_doc: function(value) {
|
||||
return value ?
|
||||
`<span class="indicator-pill green"></span>` :
|
||||
`<span class="indicator-pill gray"></span>`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Purchase Invoice Form
|
||||
frappe.ui.form.on('Purchase Invoice', {
|
||||
refresh: function(frm) {
|
||||
|
|
@ -1629,28 +1579,4 @@ frappe.ui.form.on('Purchase Invoice', {
|
|||
onload: function(frm) {
|
||||
frm.set_df_property('is_taxes_doc', 'read_only', 1);
|
||||
}
|
||||
});
|
||||
|
||||
// Purchase Invoice List
|
||||
frappe.listview_settings['Purchase Invoice'] = {
|
||||
onload: function(listview) {
|
||||
listview.columns.push({
|
||||
type: 'Check',
|
||||
df: {
|
||||
label: __('Tax Doc'),
|
||||
fieldname: 'is_taxes_doc'
|
||||
},
|
||||
width: 120
|
||||
});
|
||||
|
||||
listview.refresh();
|
||||
},
|
||||
|
||||
formatters: {
|
||||
is_taxes_doc: function(value) {
|
||||
return value ?
|
||||
`<span class="indicator-pill green"></span>` :
|
||||
`<span class="indicator-pill gray"></span>`;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -2218,55 +2218,3 @@ function show_etaxes_details(frm) {
|
|||
message: details_html
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* List view settings
|
||||
*/
|
||||
frappe.listview_settings['Sales Invoice'] = {
|
||||
// Make sure both signals reach the formatter, even though the column is bound
|
||||
// to only one of them.
|
||||
add_fields: ['is_taxes_doc', 'etaxes_send_status'],
|
||||
|
||||
onload: function(listview) {
|
||||
// Single universal E-Taxes column. It reflects everything related to the
|
||||
// tax portal for this document: whether it was loaded from E-Taxes,
|
||||
// whether a draft was created there, or whether it was sent and signed.
|
||||
listview.columns.push({
|
||||
type: 'Select',
|
||||
df: {
|
||||
label: __('E-Taxes'),
|
||||
fieldname: 'etaxes_send_status'
|
||||
},
|
||||
width: 170
|
||||
});
|
||||
|
||||
// Force refresh list with the new column
|
||||
listview.refresh();
|
||||
},
|
||||
|
||||
formatters: {
|
||||
// Universal E-Taxes status. The formatter receives the full row as its
|
||||
// third argument (value, df, doc), so we combine the send status with the
|
||||
// "loaded from E-Taxes" flag into a single indicator.
|
||||
etaxes_send_status: function(value, df, doc) {
|
||||
const send_status = doc.etaxes_send_status;
|
||||
|
||||
// Outgoing flow (document sent to the portal) takes priority — it is
|
||||
// the most specific state.
|
||||
if (send_status === 'Sent and Signed') {
|
||||
return `<span class="indicator-pill green">${__('Sent to E-Taxes')}</span>`;
|
||||
}
|
||||
if (send_status === 'Created, not signed') {
|
||||
return `<span class="indicator-pill orange">${__('Draft on E-Taxes')}</span>`;
|
||||
}
|
||||
|
||||
// Incoming flow (document imported from the portal).
|
||||
if (doc.is_taxes_doc) {
|
||||
return `<span class="indicator-pill blue">${__('Loaded from E-Taxes')}</span>`;
|
||||
}
|
||||
|
||||
// Not related to the tax portal — keep the cell clean.
|
||||
return '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
// List view settings for Sales Invoice. Loaded only via doctype_list_js. Do NOT add frappe.ui.form.on(...) here.
|
||||
|
||||
frappe.listview_settings['Sales Invoice'] = {
|
||||
// Make sure both signals reach the formatter, even though the column is bound
|
||||
// to only one of them.
|
||||
add_fields: ['is_taxes_doc', 'etaxes_send_status'],
|
||||
|
||||
onload: function(listview) {
|
||||
// Single universal E-Taxes column. It reflects everything related to the
|
||||
// tax portal for this document: whether it was loaded from E-Taxes,
|
||||
// whether a draft was created there, or whether it was sent and signed.
|
||||
listview.columns.push({
|
||||
type: 'Select',
|
||||
df: {
|
||||
label: __('E-Taxes'),
|
||||
fieldname: 'etaxes_send_status'
|
||||
},
|
||||
width: 170
|
||||
});
|
||||
|
||||
// Force refresh list with the new column
|
||||
listview.refresh();
|
||||
},
|
||||
|
||||
formatters: {
|
||||
// Universal E-Taxes status. The formatter receives the full row as its
|
||||
// third argument (value, df, doc), so we combine the send status with the
|
||||
// "loaded from E-Taxes" flag into a single indicator.
|
||||
etaxes_send_status: function(value, df, doc) {
|
||||
const send_status = doc.etaxes_send_status;
|
||||
|
||||
// Outgoing flow (document sent to the portal) takes priority — it is
|
||||
// the most specific state.
|
||||
if (send_status === 'Sent and Signed') {
|
||||
return `<span class="indicator-pill green">${__('Sent to E-Taxes')}</span>`;
|
||||
}
|
||||
if (send_status === 'Created, not signed') {
|
||||
return `<span class="indicator-pill orange">${__('Draft on E-Taxes')}</span>`;
|
||||
}
|
||||
|
||||
// Incoming flow (document imported from the portal).
|
||||
if (doc.is_taxes_doc) {
|
||||
return `<span class="indicator-pill blue">${__('Loaded from E-Taxes')}</span>`;
|
||||
}
|
||||
|
||||
// Not related to the tax portal — keep the cell clean.
|
||||
return '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1638,56 +1638,6 @@ frappe.ui.form.on('Sales Order', {
|
|||
}
|
||||
});
|
||||
|
||||
frappe.listview_settings['Sales Order'] = {
|
||||
add_fields: ['customer', 'transaction_date', 'status', 'docstatus', 'is_taxes_doc', 'taxes_doc'],
|
||||
|
||||
get_indicator: function(doc) {
|
||||
if (doc.status === 'Closed') {
|
||||
return [__('Closed'), 'green', 'status,=,Closed'];
|
||||
} else if (doc.status === 'On Hold') {
|
||||
return [__('On Hold'), 'orange', 'status,=,On Hold'];
|
||||
} else if (doc.status === 'Delivered') {
|
||||
return [__('Delivered'), 'green', 'status,=,Delivered'];
|
||||
} else if (doc.docstatus == 1) {
|
||||
return [__('To Deliver and Bill'), 'orange', 'docstatus,=,1'];
|
||||
} else if (doc.docstatus == 0) {
|
||||
return [__('Draft'), 'red', 'docstatus,=,0'];
|
||||
} else if (doc.docstatus == 2) {
|
||||
return [__('Cancelled'), 'grey', 'docstatus,=,2'];
|
||||
}
|
||||
},
|
||||
|
||||
onload: function(listview) {
|
||||
// ДОБАВЛЕНО: кнопка импорта из E-Taxes для Sales Order
|
||||
listview.page.add_inner_button(__('Import from E-Taxes'), function() {
|
||||
SalesETaxes.import.showDialog();
|
||||
}, __('E-Taxes'));
|
||||
|
||||
listview.page.add_inner_button(__('Open Settings'), function() {
|
||||
frappe.set_route('Form', 'E-Taxes Settings');
|
||||
}, __('E-Taxes'));
|
||||
|
||||
listview.columns.push({
|
||||
type: 'Check',
|
||||
df: {
|
||||
label: __('Tax Doc'),
|
||||
fieldname: 'is_taxes_doc'
|
||||
},
|
||||
width: 120
|
||||
});
|
||||
|
||||
listview.refresh();
|
||||
},
|
||||
|
||||
formatters: {
|
||||
is_taxes_doc: function(value) {
|
||||
return value ?
|
||||
`<span class="indicator-pill green"></span>` :
|
||||
`<span class="indicator-pill gray"></span>`;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Sales Invoice Form
|
||||
frappe.ui.form.on('Sales Invoice', {
|
||||
refresh: function(frm) {
|
||||
|
|
@ -1700,32 +1650,4 @@ frappe.ui.form.on('Sales Invoice', {
|
|||
onload: function(frm) {
|
||||
frm.set_df_property('is_taxes_doc', 'read_only', 1);
|
||||
}
|
||||
});
|
||||
|
||||
// Sales Invoice List - УБРАНО: кнопка импорта (теперь только в Sales Order)
|
||||
frappe.listview_settings['Sales Invoice'] = {
|
||||
add_fields: ['customer', 'posting_date', 'status', 'docstatus', 'is_taxes_doc'],
|
||||
|
||||
onload: function(listview) {
|
||||
// УБРАНО: кнопка импорта - теперь импорт только через Sales Order
|
||||
|
||||
listview.columns.push({
|
||||
type: 'Check',
|
||||
df: {
|
||||
label: __('Tax Doc'),
|
||||
fieldname: 'is_taxes_doc'
|
||||
},
|
||||
width: 120
|
||||
});
|
||||
|
||||
listview.refresh();
|
||||
},
|
||||
|
||||
formatters: {
|
||||
is_taxes_doc: function(value) {
|
||||
return value ?
|
||||
`<span class="indicator-pill green"></span>` :
|
||||
`<span class="indicator-pill gray"></span>`;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -10,7 +10,6 @@ doctype_js = {
|
|||
"Purchase Invoice": "client/purchase_invoice.js",
|
||||
"Sales Order": "client/sales_order.js",
|
||||
"Sales Invoice": "client/sales_invoice.js",
|
||||
"Journal Entry": "client/journal_entry.js",
|
||||
"Supplier": "client/supplier.js",
|
||||
"Amas Employees": "client/amas_employees.js",
|
||||
"Employee": "client/employee.js",
|
||||
|
|
@ -22,12 +21,12 @@ doctype_list_js = {
|
|||
"E-Taxes Suppliers": "client/e_taxes_suppliers_list.js",
|
||||
"E-Taxes Customers": "client/e_taxes_customers_list.js",
|
||||
"E-Taxes Unit": "client/e_taxes_unit_list.js",
|
||||
"Purchase Order": "client/purchase_order.js",
|
||||
"Purchase Invoice": "client/purchase_invoice.js",
|
||||
"Sales Order": "client/sales_order.js",
|
||||
"Sales Invoice": "client/sales_invoice.js",
|
||||
"Purchase Order": "client/purchase_order_list.js",
|
||||
"Purchase Invoice": "client/purchase_invoice_list.js",
|
||||
"Sales Order": "client/sales_order_list.js",
|
||||
"Sales Invoice": "client/sales_invoice_list.js",
|
||||
"Journal Entry": "client/journal_entry.js",
|
||||
"Employee": "client/employee.js"
|
||||
"Employee": "client/employee_list.js"
|
||||
}
|
||||
|
||||
# Хуки для добавления обработчиков событий Purchase Order
|
||||
|
|
|
|||
Loading…
Reference in New Issue