diff --git a/av_tools/av_tools/delivery_note.js b/av_tools/av_tools/delivery_note.js new file mode 100644 index 0000000..5db1e40 --- /dev/null +++ b/av_tools/av_tools/delivery_note.js @@ -0,0 +1,103 @@ +// Delivery Note ctrl+q shortcut (moved from csf_tz) + +frappe.ui.keys.add_shortcut({ + shortcut: "ctrl+q", + action: () => { + const current_doc = $(".data-row.editable-row").parent().attr("data-name"); + const item_row = locals["Delivery Note Item"][current_doc]; + frappe.call({ + method: "csf_tz.custom_api.get_item_info", + args: { item_code: item_row.item_code }, + callback: function (r) { + if (r.message.length > 0) { + const d = new frappe.ui.Dialog({ + title: __("Item Balance"), + width: 600, + }); + $(``).appendTo(d.body); + const thead = $(d.body).find("thead"); + if (r.message[0].batch_no) { + r.message.sort((a, b) => a.expiry_status - b.expiry_status); + $(` + Check + Warehouse + Qty + UOM + Batch No + Expires On + Expires in Days + `).appendTo(thead); + } else { + $(` + Check + Warehouse + Qty + UOM + `).appendTo(thead); + } + r.message.forEach((element) => { + const tbody = $(d.body).find("tbody"); + const tr = $(` + + + ${element.warehouse} + ${element.actual_qty} + ${item_row.stock_uom} + + `).appendTo(tbody); + if (element.batch_no) { + $(` + ${element.batch_no} + ${element.expires_on} + ${element.expiry_status} + `).appendTo(tr); + tr.find(".check-warehouse").attr("data-batch", element.batch_no); + tr.find(".check-warehouse").attr("data-batchQty", element.actual_qty); + } + tbody.find(".check-warehouse").on("change", function () { + $("input.check-warehouse").not(this).prop("checked", false); + }); + }); + d.set_primary_action("Select", function () { + $(d.body) + .find("input:checked") + .each(function (i, input) { + frappe.model.set_value( + item_row.doctype, + item_row.name, + "warehouse", + $(input).attr("data-warehouse"), + ); + if ($(input).attr("data-batch")) { + frappe.model.set_value( + item_row.doctype, + item_row.name, + "batch_no", + $(input).attr("data-batch"), + ); + } + }); + cur_frm.rec_dialog.hide(); + cur_frm.refresh_fields(); + }); + cur_frm.rec_dialog = d; + d.show(); + } else { + frappe.show_alert({ message: __("There is No Records"), indicator: "red" }, 5); + } + }, + }); + }, + page: this.page, + description: __("Select Item Warehouse"), + ignore_inputs: true, +}); diff --git a/av_tools/av_tools/material_request.js b/av_tools/av_tools/material_request.js new file mode 100644 index 0000000..8d65ffe --- /dev/null +++ b/av_tools/av_tools/material_request.js @@ -0,0 +1,15 @@ +// Material Request ctrl+q shortcut (moved from csf_tz) + +frappe.require([ + "/assets/av_tools/js/shortcuts.js", +]); + +frappe.ui.keys.add_shortcut({ + shortcut: "ctrl+q", + action: () => { + ctrlQ("Material Request Item"); + }, + page: this.page, + description: __("Select Item Warehouse"), + ignore_inputs: true, +}); diff --git a/av_tools/av_tools/purchase_order.js b/av_tools/av_tools/purchase_order.js index 5e56139..9133f9d 100644 --- a/av_tools/av_tools/purchase_order.js +++ b/av_tools/av_tools/purchase_order.js @@ -1,3 +1,27 @@ +frappe.require([ + "/assets/av_tools/js/po_shortcuts.js", +]); + +frappe.ui.keys.add_shortcut({ + shortcut: "ctrl+i", + action: () => { + ctrlI("Purchase Order Item"); + }, + page: this.page, + description: __("Select Customer Item Price"), + ignore_inputs: true, +}); + +frappe.ui.keys.add_shortcut({ + shortcut: "ctrl+u", + action: () => { + ctrlU("Purchase Order Item"); + }, + page: this.page, + description: __("Select Item Price"), + ignore_inputs: true, +}); + frappe.ui.form.on("Purchase Order Item", { item_code: async function (frm, cdt, cdn) { await set_dynamic_price_list_rate(frm, cdt, cdn); diff --git a/av_tools/av_tools/sales_invoice.js b/av_tools/av_tools/sales_invoice.js index 8430c25..c7520cf 100644 --- a/av_tools/av_tools/sales_invoice.js +++ b/av_tools/av_tools/sales_invoice.js @@ -1,5 +1,40 @@ // Trade In feature for Sales Invoice (moved from csf_tz) +frappe.require([ + "/assets/av_tools/js/shortcuts.js", +]); + +frappe.ui.keys.add_shortcut({ + shortcut: "ctrl+q", + action: () => { + ctrlQ("Sales Invoice Item"); + }, + page: this.page, + description: __("Select Item Warehouse"), + ignore_inputs: true, +}); + +frappe.ui.keys.add_shortcut({ + shortcut: "ctrl+i", + action: () => { + ctrlI("Sales Invoice Item"); + }, + page: this.page, + description: __("Select Customer Item Price"), + ignore_inputs: true, +}); + +frappe.ui.keys.add_shortcut({ + shortcut: "ctrl+u", + action: () => { + ctrlU("Sales Invoice Item"); + }, + page: this.page, + description: __("Select Item Price"), + ignore_inputs: true, +}); + + frappe.ui.form.on("Sales Invoice", { refresh: function (frm) { frm.trigger("set_trade_in_field_visibility"); diff --git a/av_tools/av_tools/sales_order.js b/av_tools/av_tools/sales_order.js new file mode 100644 index 0000000..e0d7497 --- /dev/null +++ b/av_tools/av_tools/sales_order.js @@ -0,0 +1,35 @@ +// Sales Order shortcut bindings (moved from csf_tz) + +frappe.require([ + "/assets/av_tools/js/shortcuts.js", +]); + +frappe.ui.keys.add_shortcut({ + shortcut: "ctrl+q", + action: () => { + ctrlQ("Sales Order Item"); + }, + page: this.page, + description: __("Select Item Warehouse"), + ignore_inputs: true, +}); + +frappe.ui.keys.add_shortcut({ + shortcut: "ctrl+i", + action: () => { + ctrlI("Sales Order Item"); + }, + page: this.page, + description: __("Select Customer Item Price"), + ignore_inputs: true, +}); + +frappe.ui.keys.add_shortcut({ + shortcut: "ctrl+u", + action: () => { + ctrlU("Sales Order Item"); + }, + page: this.page, + description: __("Select Item Price"), + ignore_inputs: true, +}); diff --git a/av_tools/av_tools/stock_entry.js b/av_tools/av_tools/stock_entry.js index d0da1b2..89cb60c 100644 --- a/av_tools/av_tools/stock_entry.js +++ b/av_tools/av_tools/stock_entry.js @@ -1,3 +1,109 @@ +frappe.require([ + "/assets/av_tools/js/shortcuts.js", +]); + +frappe.ui.keys.add_shortcut({ + shortcut: "ctrl+q", + action: () => { + const current_doc = $(".data-row.editable-row").parent().attr("data-name"); + const item_row = locals["Stock Entry Detail"][current_doc]; + frappe.call({ + method: "csf_tz.custom_api.get_item_info", + args: { item_code: item_row.item_code }, + callback: function (r) { + if (r.message.length > 0) { + const d = new frappe.ui.Dialog({ + title: __("Item Balance"), + width: 600, + }); + $(``).appendTo(d.body); + const thead = $(d.body).find("thead"); + if (r.message[0].batch_no) { + r.message.sort((a, b) => a.expiry_status - b.expiry_status); + $(` + Check + Warehouse + Qty + UOM + Batch No + Expires On + Expires in Days + `).appendTo(thead); + } else { + $(` + Check + Warehouse + Qty + UOM + `).appendTo(thead); + } + r.message.forEach((element) => { + const tbody = $(d.body).find("tbody"); + const tr = $(` + + + ${element.warehouse} + ${element.actual_qty} + ${item_row.stock_uom} + + `).appendTo(tbody); + if (element.batch_no) { + $(` + ${element.batch_no} + ${element.expires_on} + ${element.expiry_status} + `).appendTo(tr); + tr.find(".check-warehouse").attr("data-batch", element.batch_no); + tr.find(".check-warehouse").attr("data-batchQty", element.actual_qty); + } + tbody.find(".check-warehouse").on("change", function () { + $("input.check-warehouse").not(this).prop("checked", false); + }); + }); + d.set_primary_action("Select", function () { + $(d.body) + .find("input:checked") + .each(function (i, input) { + frappe.model.set_value( + item_row.doctype, + item_row.name, + "s_warehouse", + $(input).attr("data-warehouse"), + ); + if ($(input).attr("data-batch")) { + frappe.model.set_value( + item_row.doctype, + item_row.name, + "batch_no", + $(input).attr("data-batch"), + ); + } + }); + cur_frm.rec_dialog.hide(); + cur_frm.refresh_fields(); + }); + cur_frm.rec_dialog = d; + d.show(); + } else { + frappe.show_alert({ message: __("There is No Records"), indicator: "red" }, 5); + } + }, + }); + }, + page: this.page, + description: __("Select Item Warehouse"), + ignore_inputs: true, +}); + frappe.ui.form.on("Stock Entry", { onload(frm) { if (frm.doc.docstatus !== 0) { diff --git a/av_tools/hooks.py b/av_tools/hooks.py index aaafa62..31f7a9e 100644 --- a/av_tools/hooks.py +++ b/av_tools/hooks.py @@ -54,8 +54,14 @@ "authotp/api/sales_invoice.js", "av_tools/sales_invoice.js", ], - "Delivery Note": "weigh_bridge/doctype/delivery_note_weighbridge_ticket.js", - "Sales Order": "weigh_bridge/doctype/sales_order_weighbridge_ticket.js", + "Delivery Note": [ + "weigh_bridge/doctype/delivery_note_weighbridge_ticket.js", + "av_tools/delivery_note.js", + ], + "Sales Order": [ + "weigh_bridge/doctype/sales_order_weighbridge_ticket.js", + "av_tools/sales_order.js", + ], "Purchase Order": [ "weigh_bridge/doctype/purchase_order_weighbridge_ticket.js", "av_tools/purchase_order.js", @@ -63,6 +69,7 @@ "Stock Entry": "av_tools/stock_entry.js", "Purchase Invoice": "weigh_bridge/doctype/purchase_invoice_weighbridge_ticket.js", "Purchase Receipt": "weigh_bridge/doctype/purchase_receipt_weighbridge_ticket.js", + "Material Request": "av_tools/material_request.js", "Customer": "authotp/api/customer.js", "Account": "av_tools/account.js", } diff --git a/av_tools/public/js/po_shortcuts.js b/av_tools/public/js/po_shortcuts.js new file mode 100644 index 0000000..8bd29ca --- /dev/null +++ b/av_tools/public/js/po_shortcuts.js @@ -0,0 +1,161 @@ +function ctrlI(TableName) { + // Get the current document details + const current_doc = $('.data-row.editable-row').parent().attr("data-name"); + const item_row = locals[TableName][current_doc]; + + // Prepare filters for the query + const filters = { + item_code: item_row.item_code, + customer: cur_frm.doc.customer || "", + currency: cur_frm.doc.currency, + company: cur_frm.doc.company + }; + + // Call the custom API to fetch data + frappe.call({ + method: "csf_tz.custom_api.get_item_prices_custom_po", + args: { filters: filters }, + callback: function (response) { + if (response.message && response.message.length > 0) { + const e = new frappe.ui.Dialog({ + title: __('Item Prices'), + width: 600 + }); + + $(``).appendTo(e.body); + + const thead = $(e.body).find('thead'); + $(` + Check + Rate + Qty + Date + Invoice + Customer + `).appendTo(thead); + + response.message.forEach(element => { + const tbody = $(e.body).find('tbody'); + const tr = $(` + + + ${element.rate} + ${element.qty} + ${element.posting_date} + ${element.invoice} + ${element.customer} + + `).appendTo(tbody); + + tbody.find('.check-rate').on('change', function () { + $('input.check-rate').not(this).prop('checked', false); + }); + }); + + e.set_primary_action("Select", function () { + $(e.body).find('input:checked').each(function (i, input) { + frappe.model.set_value(item_row.doctype, item_row.name, 'rate', $(input).attr('data-rate')); + }); + cur_frm.rec_dialog.hide(); + cur_frm.refresh_fields(); + }); + + cur_frm.rec_dialog = e; + e.show(); + } else { + frappe.msgprint({ + message: "No rates found for the given filters.", + title: "Warning", + indicator: "orange" + }); + } + }, + error: function (error) { + // Handle errors + frappe.msgprint({ + message: "Error fetching rates. Please try again.", + title: "Error", + indicator: "red" + }); + console.error(error); + } + }); +} + +function ctrlU (TableName) { + const current_doc = $('.data-row.editable-row').parent().attr("data-name"); + const item_row = locals[TableName][current_doc]; + frappe.call({ + method: 'csf_tz.custom_api.get_item_prices_po', + args: { + item_code: item_row.item_code, + currency: cur_frm.doc.currency, + company: cur_frm.doc.company + }, + callback: function (r) { + if (r.message.length > 0) { + const e = new frappe.ui.Dialog({ + title: __('Item Prices'), + width: 600 + }); + $(``).appendTo(e.body); + const thead = $(e.body).find('thead'); + $(` + Check + Rate + Qty + Date + Invoice + Customer + `).appendTo(thead); + r.message.forEach(element => { + const tbody = $(e.body).find('tbody'); + const tr = $(` + + + ${element.price} + ${element.qty} + ${element.date} + ${element.invoice} + ${element.customer} + + `).appendTo(tbody); + + tbody.find('.check-rate').on('change', function () { + $('input.check-rate').not(this).prop('checked', false); + }); + }); + e.set_primary_action("Select", function () { + $(e.body).find('input:checked').each(function (i, input) { + frappe.model.set_value(item_row.doctype, item_row.name, 'rate', $(input).attr('data-rate')); + }); + cur_frm.rec_dialog.hide(); + cur_frm.refresh_fields(); + }); + cur_frm.rec_dialog = e; + e.show(); + } + else { + frappe.show_alert({ message: __('There are no records'), indicator: 'red' }, 5); + } + } + }); +} \ No newline at end of file diff --git a/av_tools/public/js/shortcuts.js b/av_tools/public/js/shortcuts.js new file mode 100644 index 0000000..1c3df64 --- /dev/null +++ b/av_tools/public/js/shortcuts.js @@ -0,0 +1,235 @@ +// Item lookup shortcuts (moved from csf_tz to av_tools) +// API methods still live in csf_tz.custom_api — both apps must be installed. +function ctrlQ (TableName) { + const current_doc = $('.data-row.editable-row').parent().attr("data-name"); + const item_row = locals[TableName][current_doc]; + frappe.call({ + method: 'csf_tz.custom_api.get_item_info', + args: { item_code: item_row.item_code }, + callback: function (r) { + if (r.message.length > 0) { + const d = new frappe.ui.Dialog({ + title: __('Item Balance'), + width: 600 + }); + $(``).appendTo(d.body); + const thead = $(d.body).find('thead'); + if (r.message[0].batch_no) { + r.message.sort((a, b) => a.expiry_status - b.expiry_status); + $(` + Check + Warehouse + Qty + UOM + Batch No + Expires On + Expires in Days + `).appendTo(thead); + } else { + $(` + Check + Warehouse + Qty + UOM + `).appendTo(thead); + } + r.message.forEach(element => { + const tbody = $(d.body).find('tbody'); + const tr = $(` + + + ${element.warehouse} + ${element.actual_qty} + ${item_row.stock_uom} + + `).appendTo(tbody); + if (element.batch_no) { + $(` + ${element.batch_no} + ${element.expires_on} + ${element.expiry_status} + `).appendTo(tr); + tr.find('.check-warehouse').attr('data-batch', element.batch_no); + tr.find('.check-warehouse').attr('data-batchQty', element.actual_qty); + } + tbody.find('.check-warehouse').on('change', function () { + $('input.check-warehouse').not(this).prop('checked', false); + }); + }); + d.set_primary_action("Select", function () { + $(d.body).find('input:checked').each(function (i, input) { + frappe.model.set_value(item_row.doctype, item_row.name, 'warehouse', $(input).attr('data-warehouse')); + if ($(input).attr('data-batch')) { + frappe.model.set_value(item_row.doctype, item_row.name, 'batch_no', $(input).attr('data-batch')); + } + }); + cur_frm.rec_dialog.hide(); + cur_frm.refresh_fields(); + }); + cur_frm.rec_dialog = d; + d.show(); + } + else { + frappe.show_alert({ message: __('There are no records'), indicator: 'red' }, 5); + } + } + }); +} + +function ctrlI(TableName) { + // Get the current document details + const current_doc = $('.data-row.editable-row').parent().attr("data-name"); + const item_row = locals[TableName][current_doc]; + + // Prepare filters for the query + const filters = { + item_code: item_row.item_code, + customer: cur_frm.doc.customer || "", + currency: cur_frm.doc.currency, + company: cur_frm.doc.company + }; + + // Call the custom API to fetch data + frappe.call({ + method: "csf_tz.custom_api.get_item_prices_custom", + args: { filters: filters }, + callback: function (response) { + if (response.message && response.message.length > 0) { + const e = new frappe.ui.Dialog({ + title: __('Item Prices'), + width: 600 + }); + + $(``).appendTo(e.body); + + const thead = $(e.body).find('thead'); + $(` + Check + Rate + Qty + Date + Invoice + Customer + `).appendTo(thead); + + response.message.forEach(element => { + const tbody = $(e.body).find('tbody'); + const tr = $(` + + + ${element.rate} + ${element.qty} + ${element.posting_date} + ${element.invoice} + ${element.customer} + + `).appendTo(tbody); + + tbody.find('.check-rate').on('change', function () { + $('input.check-rate').not(this).prop('checked', false); + }); + }); + + e.set_primary_action("Select", function () { + $(e.body).find('input:checked').each(function (i, input) { + frappe.model.set_value(item_row.doctype, item_row.name, 'rate', $(input).attr('data-rate')); + }); + cur_frm.rec_dialog.hide(); + cur_frm.refresh_fields(); + }); + + cur_frm.rec_dialog = e; + e.show(); + } else { + frappe.show_alert({ message: __('There are no records'), indicator: 'red' }, 5); + } + } + }); +} + +function ctrlU (TableName) { + const current_doc = $('.data-row.editable-row').parent().attr("data-name"); + const item_row = locals[TableName][current_doc]; + frappe.call({ + method: 'csf_tz.custom_api.get_item_prices', + args: { + item_code: item_row.item_code, + currency: cur_frm.doc.currency, + company: cur_frm.doc.company + }, + callback: function (r) { + if (r.message.length > 0) { + const e = new frappe.ui.Dialog({ + title: __('Item Prices'), + width: 600 + }); + $(``).appendTo(e.body); + const thead = $(e.body).find('thead'); + $(` + Check + Rate + Qty + Date + Invoice + Customer + `).appendTo(thead); + r.message.forEach(element => { + const tbody = $(e.body).find('tbody'); + const tr = $(` + + + ${element.price} + ${element.qty} + ${element.date} + ${element.invoice} + ${element.customer} + + `).appendTo(tbody); + + tbody.find('.check-rate').on('change', function () { + $('input.check-rate').not(this).prop('checked', false); + }); + }); + e.set_primary_action("Select", function () { + $(e.body).find('input:checked').each(function (i, input) { + frappe.model.set_value(item_row.doctype, item_row.name, 'rate', $(input).attr('data-rate')); + }); + cur_frm.rec_dialog.hide(); + cur_frm.refresh_fields(); + }); + cur_frm.rec_dialog = e; + e.show(); + } + else { + frappe.show_alert({ message: __('There are no records'), indicator: 'red' }, 5); + } + } + }); +} \ No newline at end of file