feat: extend SelectionDialog to 11 additional DocTypes
- Add child_doctype, items_field, has_rate, warehouse_field_name, price_list_type, price_list_field constructor options to SelectionDialog - No-rate mode hides price column, price list filter, discount and total in cart (Material Request, Stock Entry, Pick List, Request for Quotation) - Stock Entry uses s_warehouse field; Pick List uses locations child table - Buying documents (Purchase Order/Invoice, Supplier Quotation, Purchase Receipt) now use buying price list filter and buying_price_list field - api.py: add skip_price param to skip price enrichment for no-rate docs - hooks.py: register 11 new doctype JS integrations Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
f7e1657f1a
commit
ed8c31e4ad
|
|
@ -11,7 +11,7 @@ from erpnext.stock.get_item_details import get_item_details
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_items_for_selection(filters=None, start=0, page_length=50, custom_filters=None):
|
def get_items_for_selection(filters=None, start=0, page_length=50, custom_filters=None, skip_price=0):
|
||||||
"""
|
"""
|
||||||
Получить список товаров с остатками и ценами для формы подбора
|
Получить список товаров с остатками и ценами для формы подбора
|
||||||
|
|
||||||
|
|
@ -33,6 +33,8 @@ def get_items_for_selection(filters=None, start=0, page_length=50, custom_filter
|
||||||
if isinstance(custom_filters, str):
|
if isinstance(custom_filters, str):
|
||||||
custom_filters = frappe.parse_json(custom_filters)
|
custom_filters = frappe.parse_json(custom_filters)
|
||||||
|
|
||||||
|
skip_price = flt(skip_price)
|
||||||
|
|
||||||
if not filters:
|
if not filters:
|
||||||
filters = {}
|
filters = {}
|
||||||
|
|
||||||
|
|
@ -166,7 +168,7 @@ def get_items_for_selection(filters=None, start=0, page_length=50, custom_filter
|
||||||
})
|
})
|
||||||
|
|
||||||
# Цены
|
# Цены
|
||||||
if price_list and company:
|
if not skip_price and price_list and company:
|
||||||
price_data = get_price_data(
|
price_data = get_price_data(
|
||||||
item.item_code,
|
item.item_code,
|
||||||
warehouse=warehouse,
|
warehouse=warehouse,
|
||||||
|
|
@ -180,7 +182,7 @@ def get_items_for_selection(filters=None, start=0, page_length=50, custom_filter
|
||||||
item_dict.update({
|
item_dict.update({
|
||||||
'price_list_rate': 0.0,
|
'price_list_rate': 0.0,
|
||||||
'rate': 0.0,
|
'rate': 0.0,
|
||||||
'currency': frappe.defaults.get_global_default('currency') or 'USD'
|
'currency': '' if skip_price else (frappe.defaults.get_global_default('currency') or 'USD')
|
||||||
})
|
})
|
||||||
|
|
||||||
result_items.append(item_dict)
|
result_items.append(item_dict)
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,18 @@ app_include_js = "/assets/selections/js/selection_dialog.js"
|
||||||
|
|
||||||
# include js in doctype views
|
# include js in doctype views
|
||||||
doctype_js = {
|
doctype_js = {
|
||||||
"Sales Invoice": "public/js/sales_invoice.js"
|
"Sales Invoice": "public/js/sales_invoice.js",
|
||||||
|
"Sales Order": "public/js/sales_order.js",
|
||||||
|
"Quotation": "public/js/quotation.js",
|
||||||
|
"Purchase Order": "public/js/purchase_order.js",
|
||||||
|
"Purchase Invoice": "public/js/purchase_invoice.js",
|
||||||
|
"Supplier Quotation": "public/js/supplier_quotation.js",
|
||||||
|
"Request for Quotation": "public/js/request_for_quotation.js",
|
||||||
|
"Delivery Note": "public/js/delivery_note.js",
|
||||||
|
"Purchase Receipt": "public/js/purchase_receipt.js",
|
||||||
|
"Material Request": "public/js/material_request.js",
|
||||||
|
"Stock Entry": "public/js/stock_entry.js",
|
||||||
|
"Pick List": "public/js/pick_list.js",
|
||||||
}
|
}
|
||||||
# doctype_list_js = {"doctype" : "public/js/doctype_list.js"}
|
# doctype_list_js = {"doctype" : "public/js/doctype_list.js"}
|
||||||
# doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"}
|
# doctype_tree_js = {"doctype" : "public/js/doctype_tree.js"}
|
||||||
|
|
|
||||||
|
|
@ -853,3 +853,11 @@ body.selection-resizing * {
|
||||||
.selection-filters::-webkit-scrollbar-thumb:hover {
|
.selection-filters::-webkit-scrollbar-thumb:hover {
|
||||||
background: var(--text-color);
|
background: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* No-rate mode: hide price-related elements */
|
||||||
|
.selection-no-rate #selection-pricelist-link,
|
||||||
|
.selection-no-rate label[for="selection-pricelist-link"],
|
||||||
|
.selection-no-rate .cart-item-discount,
|
||||||
|
.selection-no-rate .cart-total,
|
||||||
|
.selection-no-rate .cart-item-price,
|
||||||
|
.selection-no-rate .line-through { display: none !important; }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* Интеграция формы подбора с Delivery Note
|
||||||
|
*/
|
||||||
|
|
||||||
|
frappe.ui.form.on('Delivery Note', {
|
||||||
|
refresh: function(frm) {
|
||||||
|
if (frm.doc.docstatus === 0) {
|
||||||
|
setup_selection_button(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setup_selection_button(frm) {
|
||||||
|
const grid = frm.fields_dict.items.grid;
|
||||||
|
|
||||||
|
grid.wrapper.find('.grid-add-multiple-rows').remove();
|
||||||
|
|
||||||
|
$('<button type="button" class="grid-add-multiple-rows btn btn-xs btn-secondary">')
|
||||||
|
.html(__('Add Multiple'))
|
||||||
|
.insertAfter(grid.wrapper.find('.grid-add-row'))
|
||||||
|
.on('click', function(e) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
if (!frm.doc.customer) {
|
||||||
|
frappe.msgprint({
|
||||||
|
title: __('Warning'),
|
||||||
|
message: __('Please select Customer first'),
|
||||||
|
indicator: 'orange'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection_dialog = new SelectionDialog({
|
||||||
|
frm: frm,
|
||||||
|
doctype: 'Delivery Note',
|
||||||
|
child_doctype: 'Delivery Note Item',
|
||||||
|
has_rate: true
|
||||||
|
});
|
||||||
|
selection_dialog.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* Интеграция формы подбора с Material Request
|
||||||
|
*/
|
||||||
|
|
||||||
|
frappe.ui.form.on('Material Request', {
|
||||||
|
refresh: function(frm) {
|
||||||
|
if (frm.doc.docstatus === 0) {
|
||||||
|
setup_selection_button(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setup_selection_button(frm) {
|
||||||
|
const grid = frm.fields_dict.items.grid;
|
||||||
|
|
||||||
|
grid.wrapper.find('.grid-add-multiple-rows').remove();
|
||||||
|
|
||||||
|
$('<button type="button" class="grid-add-multiple-rows btn btn-xs btn-secondary">')
|
||||||
|
.html(__('Add Multiple'))
|
||||||
|
.insertAfter(grid.wrapper.find('.grid-add-row'))
|
||||||
|
.on('click', function(e) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
const selection_dialog = new SelectionDialog({
|
||||||
|
frm: frm,
|
||||||
|
doctype: 'Material Request',
|
||||||
|
child_doctype: 'Material Request Item',
|
||||||
|
has_rate: false
|
||||||
|
});
|
||||||
|
selection_dialog.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* Интеграция формы подбора с Pick List
|
||||||
|
*/
|
||||||
|
|
||||||
|
frappe.ui.form.on('Pick List', {
|
||||||
|
refresh: function(frm) {
|
||||||
|
if (frm.doc.docstatus === 0) {
|
||||||
|
setup_selection_button(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setup_selection_button(frm) {
|
||||||
|
const grid = frm.fields_dict.locations.grid;
|
||||||
|
|
||||||
|
grid.wrapper.find('.grid-add-multiple-rows').remove();
|
||||||
|
|
||||||
|
$('<button type="button" class="grid-add-multiple-rows btn btn-xs btn-secondary">')
|
||||||
|
.html(__('Add Multiple'))
|
||||||
|
.insertAfter(grid.wrapper.find('.grid-add-row'))
|
||||||
|
.on('click', function(e) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
const selection_dialog = new SelectionDialog({
|
||||||
|
frm: frm,
|
||||||
|
doctype: 'Pick List',
|
||||||
|
child_doctype: 'Pick List Item',
|
||||||
|
items_field: 'locations',
|
||||||
|
has_rate: false
|
||||||
|
});
|
||||||
|
selection_dialog.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* Интеграция формы подбора с Purchase Invoice
|
||||||
|
*/
|
||||||
|
|
||||||
|
frappe.ui.form.on('Purchase Invoice', {
|
||||||
|
refresh: function(frm) {
|
||||||
|
if (frm.doc.docstatus === 0) {
|
||||||
|
setup_selection_button(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setup_selection_button(frm) {
|
||||||
|
const grid = frm.fields_dict.items.grid;
|
||||||
|
|
||||||
|
grid.wrapper.find('.grid-add-multiple-rows').remove();
|
||||||
|
|
||||||
|
$('<button type="button" class="grid-add-multiple-rows btn btn-xs btn-secondary">')
|
||||||
|
.html(__('Add Multiple'))
|
||||||
|
.insertAfter(grid.wrapper.find('.grid-add-row'))
|
||||||
|
.on('click', function(e) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
if (!frm.doc.supplier) {
|
||||||
|
frappe.msgprint({
|
||||||
|
title: __('Warning'),
|
||||||
|
message: __('Please select Supplier first'),
|
||||||
|
indicator: 'orange'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection_dialog = new SelectionDialog({
|
||||||
|
frm: frm,
|
||||||
|
doctype: 'Purchase Invoice',
|
||||||
|
child_doctype: 'Purchase Invoice Item',
|
||||||
|
has_rate: true,
|
||||||
|
price_list_type: 'buying'
|
||||||
|
});
|
||||||
|
selection_dialog.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* Интеграция формы подбора с Purchase Order
|
||||||
|
*/
|
||||||
|
|
||||||
|
frappe.ui.form.on('Purchase Order', {
|
||||||
|
refresh: function(frm) {
|
||||||
|
if (frm.doc.docstatus === 0) {
|
||||||
|
setup_selection_button(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setup_selection_button(frm) {
|
||||||
|
const grid = frm.fields_dict.items.grid;
|
||||||
|
|
||||||
|
grid.wrapper.find('.grid-add-multiple-rows').remove();
|
||||||
|
|
||||||
|
$('<button type="button" class="grid-add-multiple-rows btn btn-xs btn-secondary">')
|
||||||
|
.html(__('Add Multiple'))
|
||||||
|
.insertAfter(grid.wrapper.find('.grid-add-row'))
|
||||||
|
.on('click', function(e) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
if (!frm.doc.supplier) {
|
||||||
|
frappe.msgprint({
|
||||||
|
title: __('Warning'),
|
||||||
|
message: __('Please select Supplier first'),
|
||||||
|
indicator: 'orange'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection_dialog = new SelectionDialog({
|
||||||
|
frm: frm,
|
||||||
|
doctype: 'Purchase Order',
|
||||||
|
child_doctype: 'Purchase Order Item',
|
||||||
|
has_rate: true,
|
||||||
|
price_list_type: 'buying'
|
||||||
|
});
|
||||||
|
selection_dialog.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* Интеграция формы подбора с Purchase Receipt
|
||||||
|
*/
|
||||||
|
|
||||||
|
frappe.ui.form.on('Purchase Receipt', {
|
||||||
|
refresh: function(frm) {
|
||||||
|
if (frm.doc.docstatus === 0) {
|
||||||
|
setup_selection_button(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setup_selection_button(frm) {
|
||||||
|
const grid = frm.fields_dict.items.grid;
|
||||||
|
|
||||||
|
grid.wrapper.find('.grid-add-multiple-rows').remove();
|
||||||
|
|
||||||
|
$('<button type="button" class="grid-add-multiple-rows btn btn-xs btn-secondary">')
|
||||||
|
.html(__('Add Multiple'))
|
||||||
|
.insertAfter(grid.wrapper.find('.grid-add-row'))
|
||||||
|
.on('click', function(e) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
if (!frm.doc.supplier) {
|
||||||
|
frappe.msgprint({
|
||||||
|
title: __('Warning'),
|
||||||
|
message: __('Please select Supplier first'),
|
||||||
|
indicator: 'orange'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection_dialog = new SelectionDialog({
|
||||||
|
frm: frm,
|
||||||
|
doctype: 'Purchase Receipt',
|
||||||
|
child_doctype: 'Purchase Receipt Item',
|
||||||
|
has_rate: true,
|
||||||
|
price_list_type: 'buying'
|
||||||
|
});
|
||||||
|
selection_dialog.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* Интеграция формы подбора с Quotation
|
||||||
|
*/
|
||||||
|
|
||||||
|
frappe.ui.form.on('Quotation', {
|
||||||
|
refresh: function(frm) {
|
||||||
|
if (frm.doc.docstatus === 0) {
|
||||||
|
setup_selection_button(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setup_selection_button(frm) {
|
||||||
|
const grid = frm.fields_dict.items.grid;
|
||||||
|
|
||||||
|
grid.wrapper.find('.grid-add-multiple-rows').remove();
|
||||||
|
|
||||||
|
$('<button type="button" class="grid-add-multiple-rows btn btn-xs btn-secondary">')
|
||||||
|
.html(__('Add Multiple'))
|
||||||
|
.insertAfter(grid.wrapper.find('.grid-add-row'))
|
||||||
|
.on('click', function(e) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
if (!frm.doc.party_name) {
|
||||||
|
frappe.msgprint({
|
||||||
|
title: __('Warning'),
|
||||||
|
message: __('Please select Party first'),
|
||||||
|
indicator: 'orange'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection_dialog = new SelectionDialog({
|
||||||
|
frm: frm,
|
||||||
|
doctype: 'Quotation',
|
||||||
|
child_doctype: 'Quotation Item',
|
||||||
|
has_rate: true
|
||||||
|
});
|
||||||
|
selection_dialog.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/**
|
||||||
|
* Интеграция формы подбора с Request for Quotation
|
||||||
|
*/
|
||||||
|
|
||||||
|
frappe.ui.form.on('Request for Quotation', {
|
||||||
|
refresh: function(frm) {
|
||||||
|
if (frm.doc.docstatus === 0) {
|
||||||
|
setup_selection_button(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setup_selection_button(frm) {
|
||||||
|
const grid = frm.fields_dict.items.grid;
|
||||||
|
|
||||||
|
grid.wrapper.find('.grid-add-multiple-rows').remove();
|
||||||
|
|
||||||
|
$('<button type="button" class="grid-add-multiple-rows btn btn-xs btn-secondary">')
|
||||||
|
.html(__('Add Multiple'))
|
||||||
|
.insertAfter(grid.wrapper.find('.grid-add-row'))
|
||||||
|
.on('click', function(e) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
const selection_dialog = new SelectionDialog({
|
||||||
|
frm: frm,
|
||||||
|
doctype: 'Request for Quotation',
|
||||||
|
child_doctype: 'Request for Quotation Item',
|
||||||
|
has_rate: false
|
||||||
|
});
|
||||||
|
selection_dialog.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -38,7 +38,8 @@ function setup_selection_button(frm) {
|
||||||
|
|
||||||
const selection_dialog = new SelectionDialog({
|
const selection_dialog = new SelectionDialog({
|
||||||
frm: frm,
|
frm: frm,
|
||||||
doctype: 'Sales Invoice'
|
doctype: 'Sales Invoice',
|
||||||
|
child_doctype: 'Sales Invoice Item'
|
||||||
});
|
});
|
||||||
selection_dialog.show();
|
selection_dialog.show();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
/**
|
||||||
|
* Интеграция формы подбора с Sales Order
|
||||||
|
*/
|
||||||
|
|
||||||
|
frappe.ui.form.on('Sales Order', {
|
||||||
|
refresh: function(frm) {
|
||||||
|
if (frm.doc.docstatus === 0) {
|
||||||
|
setup_selection_button(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setup_selection_button(frm) {
|
||||||
|
const grid = frm.fields_dict.items.grid;
|
||||||
|
|
||||||
|
grid.wrapper.find('.grid-add-multiple-rows').remove();
|
||||||
|
|
||||||
|
$('<button type="button" class="grid-add-multiple-rows btn btn-xs btn-secondary">')
|
||||||
|
.html(__('Add Multiple'))
|
||||||
|
.insertAfter(grid.wrapper.find('.grid-add-row'))
|
||||||
|
.on('click', function(e) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
if (!frm.doc.customer) {
|
||||||
|
frappe.msgprint({
|
||||||
|
title: __('Warning'),
|
||||||
|
message: __('Please select Customer first'),
|
||||||
|
indicator: 'orange'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection_dialog = new SelectionDialog({
|
||||||
|
frm: frm,
|
||||||
|
doctype: 'Sales Order',
|
||||||
|
child_doctype: 'Sales Order Item',
|
||||||
|
has_rate: true
|
||||||
|
});
|
||||||
|
selection_dialog.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,12 @@ class SelectionDialog {
|
||||||
constructor(opts) {
|
constructor(opts) {
|
||||||
this.frm = opts.frm;
|
this.frm = opts.frm;
|
||||||
this.doctype = opts.doctype;
|
this.doctype = opts.doctype;
|
||||||
|
this.child_doctype = opts.child_doctype;
|
||||||
|
this.items_field = opts.items_field || 'items';
|
||||||
|
this.has_rate = opts.has_rate !== false;
|
||||||
|
this.warehouse_field_name = opts.warehouse_field_name || 'warehouse';
|
||||||
|
this.price_list_type = opts.price_list_type || 'selling';
|
||||||
|
this.price_list_field = opts.price_list_field || (this.price_list_type === 'buying' ? 'buying_price_list' : 'selling_price_list');
|
||||||
this.cart = {}; // { item_code: {item_code, item_name, qty, rate, discount_pct, ...} }
|
this.cart = {}; // { item_code: {item_code, item_name, qty, rate, discount_pct, ...} }
|
||||||
this.items_data = [];
|
this.items_data = [];
|
||||||
this.current_page = 0;
|
this.current_page = 0;
|
||||||
|
|
@ -60,10 +66,10 @@ class SelectionDialog {
|
||||||
<label>${__('Warehouse')}</label>
|
<label>${__('Warehouse')}</label>
|
||||||
<div id="selection-warehouse-link"></div>
|
<div id="selection-warehouse-link"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="filter-group">
|
${this.has_rate ? `<div class="filter-group">
|
||||||
<label>${__('Price List')}</label>
|
<label>${__('Price List')}</label>
|
||||||
<div id="selection-pricelist-link"></div>
|
<div id="selection-pricelist-link"></div>
|
||||||
</div>
|
</div>` : ''}
|
||||||
<div class="filter-group">
|
<div class="filter-group">
|
||||||
<div class="selection-stock-toggle">
|
<div class="selection-stock-toggle">
|
||||||
<label class="stock-toggle-label">
|
<label class="stock-toggle-label">
|
||||||
|
|
@ -179,6 +185,9 @@ class SelectionDialog {
|
||||||
|
|
||||||
// Add unique class to modal wrapper for CSS targeting
|
// Add unique class to modal wrapper for CSS targeting
|
||||||
this.dialog.$wrapper.addClass('selection-dialog-wrapper');
|
this.dialog.$wrapper.addClass('selection-dialog-wrapper');
|
||||||
|
if (!this.has_rate) {
|
||||||
|
this.dialog.$wrapper.addClass('selection-no-rate');
|
||||||
|
}
|
||||||
|
|
||||||
// Force fullscreen with setTimeout to override Frappe's styles
|
// Force fullscreen with setTimeout to override Frappe's styles
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
@ -486,13 +495,14 @@ class SelectionDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_pricelist_field() {
|
setup_pricelist_field() {
|
||||||
|
if (!this.has_rate) return;
|
||||||
const me = this;
|
const me = this;
|
||||||
this.pricelist_field = frappe.ui.form.make_control({
|
this.pricelist_field = frappe.ui.form.make_control({
|
||||||
df: {
|
df: {
|
||||||
fieldtype: 'Link',
|
fieldtype: 'Link',
|
||||||
options: 'Price List',
|
options: 'Price List',
|
||||||
placeholder: __('Select Price List...'),
|
placeholder: __('Select Price List...'),
|
||||||
filters: { selling: 1, enabled: 1 },
|
filters: { [me.price_list_type]: 1, enabled: 1 },
|
||||||
onchange: function() {
|
onchange: function() {
|
||||||
me.filters.price_list = this.value || '';
|
me.filters.price_list = this.value || '';
|
||||||
me.current_page = 0;
|
me.current_page = 0;
|
||||||
|
|
@ -503,7 +513,7 @@ class SelectionDialog {
|
||||||
render_input: true,
|
render_input: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
const default_price_list = me.frm.doc.selling_price_list || '';
|
const default_price_list = me.frm.doc[me.price_list_field] || '';
|
||||||
if (default_price_list) {
|
if (default_price_list) {
|
||||||
me.pricelist_field.set_value(default_price_list);
|
me.pricelist_field.set_value(default_price_list);
|
||||||
me.filters.price_list = default_price_list;
|
me.filters.price_list = default_price_list;
|
||||||
|
|
@ -571,7 +581,7 @@ class SelectionDialog {
|
||||||
item_group: this.filters.item_group || '',
|
item_group: this.filters.item_group || '',
|
||||||
warehouse: this.filters.warehouse || this.frm.doc.set_warehouse || '',
|
warehouse: this.filters.warehouse || this.frm.doc.set_warehouse || '',
|
||||||
customer: this.frm.doc.customer || '',
|
customer: this.frm.doc.customer || '',
|
||||||
price_list: this.filters.price_list || this.frm.doc.selling_price_list || '',
|
price_list: this.filters.price_list || this.frm.doc[this.price_list_field] || '',
|
||||||
company: this.frm.doc.company || '',
|
company: this.frm.doc.company || '',
|
||||||
transaction_date: this.frm.doc.posting_date || frappe.datetime.get_today(),
|
transaction_date: this.frm.doc.posting_date || frappe.datetime.get_today(),
|
||||||
only_in_stock: this.filters.only_in_stock || false,
|
only_in_stock: this.filters.only_in_stock || false,
|
||||||
|
|
@ -597,7 +607,8 @@ class SelectionDialog {
|
||||||
page_length: this.page_length,
|
page_length: this.page_length,
|
||||||
custom_filters: this.custom_filters.filter(cf =>
|
custom_filters: this.custom_filters.filter(cf =>
|
||||||
cf.field && (cf.operator === 'is set' || cf.operator === 'is not set' || cf.value !== '')
|
cf.field && (cf.operator === 'is set' || cf.operator === 'is not set' || cf.value !== '')
|
||||||
)
|
),
|
||||||
|
skip_price: this.has_rate ? 0 : 1
|
||||||
},
|
},
|
||||||
callback: function(r) {
|
callback: function(r) {
|
||||||
if (r.message) {
|
if (r.message) {
|
||||||
|
|
@ -627,7 +638,7 @@ class SelectionDialog {
|
||||||
const columns = [
|
const columns = [
|
||||||
{ key: 'item_name', label: __('Name'), sortable: true },
|
{ key: 'item_name', label: __('Name'), sortable: true },
|
||||||
{ key: 'available_qty', label: __('Stock'), sortable: true },
|
{ key: 'available_qty', label: __('Stock'), sortable: true },
|
||||||
{ key: 'rate', label: __('Price'), sortable: true },
|
...(this.has_rate ? [{ key: 'rate', label: __('Price'), sortable: true }] : []),
|
||||||
{ key: 'qty_action', label: __('Qty / Add'), sortable: false }
|
{ key: 'qty_action', label: __('Qty / Add'), sortable: false }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -683,9 +694,7 @@ class SelectionDialog {
|
||||||
${stock_text} ${item.stock_uom || ''}
|
${stock_text} ${item.stock_uom || ''}
|
||||||
</span>
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
${me.has_rate ? `<td><strong>${me.format_currency(item.rate, item.currency)}</strong></td>` : ''}
|
||||||
<strong>${me.format_currency(item.rate, item.currency)}</strong>
|
|
||||||
</td>
|
|
||||||
<td class="action-cell">
|
<td class="action-cell">
|
||||||
<div class="row-action-group">
|
<div class="row-action-group">
|
||||||
<input type="text" inputmode="decimal" class="form-control input-sm row-qty-input"
|
<input type="text" inputmode="decimal" class="form-control input-sm row-qty-input"
|
||||||
|
|
@ -1414,7 +1423,7 @@ class SelectionDialog {
|
||||||
}
|
}
|
||||||
|
|
||||||
cart_array.forEach(item => {
|
cart_array.forEach(item => {
|
||||||
const row = frappe.model.add_child(me.frm.doc, 'Sales Invoice Item', 'items');
|
const row = frappe.model.add_child(me.frm.doc, me.child_doctype, me.items_field);
|
||||||
row.item_code = item.item_code;
|
row.item_code = item.item_code;
|
||||||
row.item_name = item.item_name;
|
row.item_name = item.item_name;
|
||||||
row.qty = item.qty;
|
row.qty = item.qty;
|
||||||
|
|
@ -1422,19 +1431,21 @@ class SelectionDialog {
|
||||||
row.uom = item.stock_uom;
|
row.uom = item.stock_uom;
|
||||||
row.conversion_factor = 1;
|
row.conversion_factor = 1;
|
||||||
|
|
||||||
const rate = flt(item.rate || 0);
|
if (me.has_rate) {
|
||||||
const discount = flt(item.discount_pct || 0);
|
const rate = flt(item.rate || 0);
|
||||||
const discounted_rate = discount > 0 ? flt(rate * (1 - discount / 100), 2) : rate;
|
const discount = flt(item.discount_pct || 0);
|
||||||
|
const discounted_rate = discount > 0 ? flt(rate * (1 - discount / 100), 2) : rate;
|
||||||
|
|
||||||
row.price_list_rate = rate;
|
row.price_list_rate = rate;
|
||||||
row.discount_percentage = discount > 0 ? discount : 0;
|
row.discount_percentage = discount > 0 ? discount : 0;
|
||||||
row.rate = discounted_rate;
|
row.rate = discounted_rate;
|
||||||
row.amount = flt(item.qty * discounted_rate, 2);
|
row.amount = flt(item.qty * discounted_rate, 2);
|
||||||
|
}
|
||||||
|
|
||||||
if (item.warehouse) row.warehouse = item.warehouse;
|
if (item.warehouse) row[me.warehouse_field_name] = item.warehouse;
|
||||||
});
|
});
|
||||||
|
|
||||||
me.frm.refresh_field('items');
|
me.frm.refresh_field(me.items_field);
|
||||||
me.dialog.hide();
|
me.dialog.hide();
|
||||||
|
|
||||||
frappe.show_alert({
|
frappe.show_alert({
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* Интеграция формы подбора с Stock Entry
|
||||||
|
*/
|
||||||
|
|
||||||
|
frappe.ui.form.on('Stock Entry', {
|
||||||
|
refresh: function(frm) {
|
||||||
|
if (frm.doc.docstatus === 0) {
|
||||||
|
setup_selection_button(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setup_selection_button(frm) {
|
||||||
|
const grid = frm.fields_dict.items.grid;
|
||||||
|
|
||||||
|
grid.wrapper.find('.grid-add-multiple-rows').remove();
|
||||||
|
|
||||||
|
$('<button type="button" class="grid-add-multiple-rows btn btn-xs btn-secondary">')
|
||||||
|
.html(__('Add Multiple'))
|
||||||
|
.insertAfter(grid.wrapper.find('.grid-add-row'))
|
||||||
|
.on('click', function(e) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
const selection_dialog = new SelectionDialog({
|
||||||
|
frm: frm,
|
||||||
|
doctype: 'Stock Entry',
|
||||||
|
child_doctype: 'Stock Entry Detail',
|
||||||
|
has_rate: false,
|
||||||
|
warehouse_field_name: 's_warehouse'
|
||||||
|
});
|
||||||
|
selection_dialog.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
/**
|
||||||
|
* Интеграция формы подбора с Supplier Quotation
|
||||||
|
*/
|
||||||
|
|
||||||
|
frappe.ui.form.on('Supplier Quotation', {
|
||||||
|
refresh: function(frm) {
|
||||||
|
if (frm.doc.docstatus === 0) {
|
||||||
|
setup_selection_button(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function setup_selection_button(frm) {
|
||||||
|
const grid = frm.fields_dict.items.grid;
|
||||||
|
|
||||||
|
grid.wrapper.find('.grid-add-multiple-rows').remove();
|
||||||
|
|
||||||
|
$('<button type="button" class="grid-add-multiple-rows btn btn-xs btn-secondary">')
|
||||||
|
.html(__('Add Multiple'))
|
||||||
|
.insertAfter(grid.wrapper.find('.grid-add-row'))
|
||||||
|
.on('click', function(e) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
|
||||||
|
if (!frm.doc.supplier) {
|
||||||
|
frappe.msgprint({
|
||||||
|
title: __('Warning'),
|
||||||
|
message: __('Please select Supplier first'),
|
||||||
|
indicator: 'orange'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection_dialog = new SelectionDialog({
|
||||||
|
frm: frm,
|
||||||
|
doctype: 'Supplier Quotation',
|
||||||
|
child_doctype: 'Supplier Quotation Item',
|
||||||
|
has_rate: true,
|
||||||
|
price_list_type: 'buying'
|
||||||
|
});
|
||||||
|
selection_dialog.show();
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue